Merge lp:~nataliabidart/ubuntu-sso-client/stable-3-0-update-2.99.91 into lp:ubuntu-sso-client/stable-3-0
- stable-3-0-update-2.99.91
- Merge into stable-3-0
Status: | Merged |
---|---|
Approved by: | Natalia Bidart |
Approved revision: | 831 |
Merged at revision: | 830 |
Proposed branch: | lp:~nataliabidart/ubuntu-sso-client/stable-3-0-update-2.99.91 |
Merge into: | lp:ubuntu-sso-client/stable-3-0 |
Diff against target: |
5121 lines (+2191/-925) 62 files modified
bin/ubuntu-sso-login-qt (+6/-3) bin/ubuntu-sso-proxy-creds-qt (+6/-3) data/qt/linux.qss (+2/-0) data/qt/loadingoverlay.ui (+0/-5) data/qt/proxy_credentials_dialog.ui (+0/-2) data/qt/reset_password.ui (+199/-262) data/qt/resources.qrc (+2/-0) data/qt/setup_account.ui (+1/-36) data/qt/ssl_dialog.ui (+0/-5) data/qt/stylesheet.qss (+1/-13) data/qt/windows.qss (+5/-0) po/POTFILES.in (+1/-0) run-tests (+1/-1) ubuntu_sso/__init__.py (+8/-0) ubuntu_sso/gtk/gui.py (+18/-33) ubuntu_sso/gtk/tests/test_gui.py (+34/-5) ubuntu_sso/qt/__init__.py (+33/-5) ubuntu_sso/qt/common.py (+3/-3) ubuntu_sso/qt/current_user_sign_in_page.py (+13/-12) ubuntu_sso/qt/email_verification_page.py (+8/-5) ubuntu_sso/qt/enhanced_check_box.py (+9/-3) ubuntu_sso/qt/forgotten_password_page.py (+5/-1) ubuntu_sso/qt/loadingoverlay.py (+3/-1) ubuntu_sso/qt/main.py (+12/-3) ubuntu_sso/qt/network_detection_page.py (+5/-1) ubuntu_sso/qt/proxy_dialog.py (+4/-7) ubuntu_sso/qt/reset_password_page.py (+6/-2) ubuntu_sso/qt/setup_account_page.py (+19/-18) ubuntu_sso/qt/sso_wizard_page.py (+116/-76) ubuntu_sso/qt/tests/__init__.py (+55/-7) ubuntu_sso/qt/tests/login_u_p.py (+0/-2) ubuntu_sso/qt/tests/show_gui.py (+0/-2) ubuntu_sso/qt/tests/test_common.py (+130/-2) ubuntu_sso/qt/tests/test_current_user_sign_in_page.py (+28/-17) ubuntu_sso/qt/tests/test_email_verification.py (+6/-7) ubuntu_sso/qt/tests/test_enhanced_check_box.py (+19/-1) ubuntu_sso/qt/tests/test_forgotten_password.py (+6/-4) ubuntu_sso/qt/tests/test_loadingoverlay.py (+0/-3) ubuntu_sso/qt/tests/test_main.py (+108/-12) ubuntu_sso/qt/tests/test_proxy_dialog.py (+2/-2) ubuntu_sso/qt/tests/test_reset_password.py (+9/-17) ubuntu_sso/qt/tests/test_setup_account.py (+55/-71) ubuntu_sso/qt/tests/test_ssl_dialog.py (+2/-1) ubuntu_sso/qt/tests/test_sso_wizard_page.py (+98/-59) ubuntu_sso/qt/tests/test_ubuntu_sso_wizard.py (+36/-6) ubuntu_sso/qt/ubuntu_sso_wizard.py (+19/-2) ubuntu_sso/utils/__init__.py (+15/-2) ubuntu_sso/utils/linux.py (+19/-0) ubuntu_sso/utils/runner/glib.py (+1/-1) ubuntu_sso/utils/runner/tx.py (+1/-1) ubuntu_sso/utils/tests/test_common.py (+10/-1) ubuntu_sso/utils/ui.py (+7/-1) ubuntu_sso/utils/webclient/__init__.py (+18/-2) ubuntu_sso/utils/webclient/common.py (+110/-2) ubuntu_sso/utils/webclient/gsettings.py (+21/-14) ubuntu_sso/utils/webclient/libsoup.py (+48/-4) ubuntu_sso/utils/webclient/qtnetwork.py (+124/-22) ubuntu_sso/utils/webclient/tests/__init__.py (+74/-6) ubuntu_sso/utils/webclient/tests/test_gsettings.py (+44/-61) ubuntu_sso/utils/webclient/tests/test_webclient.py (+510/-10) ubuntu_sso/utils/webclient/txweb.py (+77/-78) ubuntu_sso/utils/windows.py (+19/-0) |
To merge this branch: | bzr merge lp:~nataliabidart/ubuntu-sso-client/stable-3-0-update-2.99.91 |
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Roberto Alsina (community) | Approve | ||
Review via email: mp+98440@code.launchpad.net |
Commit message
- Updating from trunk up to revno 930:
[ Alejandro J. Cura <email address hidden> ]
- Do not allow ssl errors to be ignored (LP: #959390).
- Handle wrong credentials properly in qtnetwork webclient
(LP: #957317).
- Use HTTPClientFactory to allow replacing the reactor used to connect
(LP: #929207).
[ Diego Sarmentero <email address hidden> ]
- Decode the content of help_text (LP: #951371).
- Removed the title from the reset page.
- Adding missing file for translation (LP: #951376).
- Adding a general error message when the argument received by
build_
- Adding some checks to setup_page (LP: #951461).
- Adding a padding to the right margin of the reset layout, and align
one of its layout to the left (LP: #945065).
- Executing hide_error when the user click the refresh captcha link,
not inside of the _refresh_captcha method, because this is executed
automatically when a captcha error is generated, so we will always
miss the error message (LP: #955010).
- Removing the align property from the label that wasn't necessary
and was breakint the work wrap. Also adjust the height of the widget
depending if it has more than one line (LP: #940392).
- Improve logging operations (LP: #934500).
- Making LINK_STYLE to be unicode (LP: #950953).
- Setting the window title equal to the app_name (LP: #949744).
- The _move_to_
that the signal emits (LP: #945066).
[ Jeremy Bicha <email address hidden> ]
- Improve the grammar for the CLOSE_AND_
(LP: #949978).
[ Manuel de la Pena <email address hidden> ]
- Changed the way in which we deal with proxies to work around bugs
found in the QNetworkAccessM
- Stopped listening to the proxyAuthentica
dialog showing more than once (LP: #957170)
- Made changes in the way the webclient is selected to ensure that qt
is used when possible (LP: #957169)
- Connected the WebKit browser correctly so that the tc page gets
loaded (LP: #933081).
- Added code to check if the browser with the t&c was already loaded.
If it is just show the t&c page, otherwise perform the request
(LP: #956185).
- Added a translatable string to give more context of the ssl cert
info to the user (LP: #948119).
- Provided the logic required for the Qt webclient implementation to
detect ssl errors and spawn the ssl dialog to allow the user accept
the ssl cert exceptions (LP: #948134).
- Changed the qt webclient implementation to use a proxy factory so
that the correct proxy is chosen according to the request
(LP: #950088).
- Added the required code to allow the webclient use authenticated
proxies and request the creds when needed (LP: #933727).
[ Natalia B. Bidart <email address hidden> ]
- The tooltip should not be shown for titles and subtitles when
no cut off was needed (LP: #949741).
- Making the WizardHeader a reusable class.
- Added some minor logging to build_general_
- Improved code for the 'sign in' button validation.
[ Roberto Alsina <email address hidden> ]
- Made the ubuntu-
- Added .exe to the constant for binary names if needed (LP: #958778).
- Enable platform-specific styling (LP: #953318).
- Return the executable's dirname as BIN_DIR for frozen binaries
(LP: #956187).
- Only import DBus on Linux (LP: #956304).
- Fixed tests so they work under non-ascii locales (LP: #951716).
[ Rodney Dawes <email address hidden> ]
- Don't hard-code font sizes
- Remove usage of weight property as a numeric; just use bold
property instead (LP: #953062).
Description of the change
Roberto Alsina (ralsina) : | # |
Preview Diff
1 | === modified file 'bin/ubuntu-sso-login-qt' | |||
2 | --- bin/ubuntu-sso-login-qt 2012-02-13 15:43:59 +0000 | |||
3 | +++ bin/ubuntu-sso-login-qt 2012-03-20 16:10:09 +0000 | |||
4 | @@ -15,16 +15,19 @@ | |||
5 | 15 | # You should have received a copy of the GNU General Public License along | 15 | # You should have received a copy of the GNU General Public License along |
6 | 16 | # with this program. If not, see <http://www.gnu.org/licenses/>. | 16 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
7 | 17 | 17 | ||
9 | 18 | """Start the sso GTK UI.""" | 18 | """Start the sso Qt UI.""" |
10 | 19 | 19 | ||
11 | 20 | # Invalid name "ubuntu-sso-login-qt", pylint: disable=C0103 | 20 | # Invalid name "ubuntu-sso-login-qt", pylint: disable=C0103 |
12 | 21 | # Access to a protected member, pylint: disable=W0212 | 21 | # Access to a protected member, pylint: disable=W0212 |
13 | 22 | 22 | ||
14 | 23 | import sys | ||
15 | 24 | |||
16 | 23 | from ubuntu_sso.qt.main import main | 25 | from ubuntu_sso.qt.main import main |
17 | 24 | from ubuntu_sso.utils.ui import parse_args | 26 | from ubuntu_sso.utils.ui import parse_args |
18 | 25 | 27 | ||
21 | 26 | from dbus.mainloop.qt import DBusQtMainLoop | 28 | if sys.platform.startswith('linux'): |
22 | 27 | DBusQtMainLoop(set_as_default=True) | 29 | from dbus.mainloop.qt import DBusQtMainLoop |
23 | 30 | DBusQtMainLoop(set_as_default=True) | ||
24 | 28 | 31 | ||
25 | 29 | 32 | ||
26 | 30 | if __name__ == "__main__": | 33 | if __name__ == "__main__": |
27 | 31 | 34 | ||
28 | === modified file 'bin/ubuntu-sso-proxy-creds-qt' | |||
29 | --- bin/ubuntu-sso-proxy-creds-qt 2012-02-24 13:07:06 +0000 | |||
30 | +++ bin/ubuntu-sso-proxy-creds-qt 2012-03-20 16:10:09 +0000 | |||
31 | @@ -19,9 +19,12 @@ | |||
32 | 19 | 19 | ||
33 | 20 | # Invalid name, pylint: disable=C0103 | 20 | # Invalid name, pylint: disable=C0103 |
34 | 21 | 21 | ||
38 | 22 | # set the dbus main loop to be used | 22 | import sys |
39 | 23 | from dbus.mainloop.qt import DBusQtMainLoop | 23 | |
40 | 24 | DBusQtMainLoop(set_as_default=True) | 24 | if sys.platform.startswith('linux'): |
41 | 25 | # set the dbus main loop to be used | ||
42 | 26 | from dbus.mainloop.qt import DBusQtMainLoop | ||
43 | 27 | DBusQtMainLoop(set_as_default=True) | ||
44 | 25 | 28 | ||
45 | 26 | from ubuntu_sso.qt.proxy_dialog import main | 29 | from ubuntu_sso.qt.proxy_dialog import main |
46 | 27 | 30 | ||
47 | 28 | 31 | ||
48 | === added file 'data/qt/linux.qss' | |||
49 | --- data/qt/linux.qss 1970-01-01 00:00:00 +0000 | |||
50 | +++ data/qt/linux.qss 2012-03-20 16:10:09 +0000 | |||
51 | @@ -0,0 +1,2 @@ | |||
52 | 1 | /* Styles specific to the linux platform */ | ||
53 | 2 | |||
54 | 0 | 3 | ||
55 | === modified file 'data/qt/loadingoverlay.ui' | |||
56 | --- data/qt/loadingoverlay.ui 2012-02-22 16:58:08 +0000 | |||
57 | +++ data/qt/loadingoverlay.ui 2012-03-20 16:10:09 +0000 | |||
58 | @@ -52,11 +52,6 @@ | |||
59 | 52 | <verstretch>0</verstretch> | 52 | <verstretch>0</verstretch> |
60 | 53 | </sizepolicy> | 53 | </sizepolicy> |
61 | 54 | </property> | 54 | </property> |
62 | 55 | <property name="font"> | ||
63 | 56 | <font> | ||
64 | 57 | <pointsize>14</pointsize> | ||
65 | 58 | </font> | ||
66 | 59 | </property> | ||
67 | 60 | <property name="text"> | 55 | <property name="text"> |
68 | 61 | <string notr="true">Getting information, please wait...</string> | 56 | <string notr="true">Getting information, please wait...</string> |
69 | 62 | </property> | 57 | </property> |
70 | 63 | 58 | ||
71 | === modified file 'data/qt/proxy_credentials_dialog.ui' | |||
72 | --- data/qt/proxy_credentials_dialog.ui 2012-02-23 11:47:00 +0000 | |||
73 | +++ data/qt/proxy_credentials_dialog.ui 2012-03-20 16:10:09 +0000 | |||
74 | @@ -113,8 +113,6 @@ | |||
75 | 113 | </property> | 113 | </property> |
76 | 114 | <property name="font"> | 114 | <property name="font"> |
77 | 115 | <font> | 115 | <font> |
78 | 116 | <pointsize>14</pointsize> | ||
79 | 117 | <weight>75</weight> | ||
80 | 118 | <bold>true</bold> | 116 | <bold>true</bold> |
81 | 119 | </font> | 117 | </font> |
82 | 120 | </property> | 118 | </property> |
83 | 121 | 119 | ||
84 | === modified file 'data/qt/reset_password.ui' | |||
85 | --- data/qt/reset_password.ui 2012-03-05 20:30:57 +0000 | |||
86 | +++ data/qt/reset_password.ui 2012-03-20 16:10:09 +0000 | |||
87 | @@ -6,14 +6,14 @@ | |||
88 | 6 | <rect> | 6 | <rect> |
89 | 7 | <x>0</x> | 7 | <x>0</x> |
90 | 8 | <y>0</y> | 8 | <y>0</y> |
93 | 9 | <width>544</width> | 9 | <width>505</width> |
94 | 10 | <height>280</height> | 10 | <height>260</height> |
95 | 11 | </rect> | 11 | </rect> |
96 | 12 | </property> | 12 | </property> |
99 | 13 | <property name="layoutDirection"> | 13 | <property name="windowTitle"> |
100 | 14 | <enum>Qt::LeftToRight</enum> | 14 | <string/> |
101 | 15 | </property> | 15 | </property> |
103 | 16 | <layout class="QVBoxLayout" name="verticalLayout_6"> | 16 | <layout class="QVBoxLayout" name="verticalLayout_4"> |
104 | 17 | <property name="spacing"> | 17 | <property name="spacing"> |
105 | 18 | <number>15</number> | 18 | <number>15</number> |
106 | 19 | </property> | 19 | </property> |
107 | @@ -21,261 +21,215 @@ | |||
108 | 21 | <number>0</number> | 21 | <number>0</number> |
109 | 22 | </property> | 22 | </property> |
110 | 23 | <item> | 23 | <item> |
303 | 24 | <layout class="QHBoxLayout" name="horizontalLayout"> | 24 | <layout class="QGridLayout" name="gridLayout"> |
304 | 25 | <property name="spacing"> | 25 | <item row="0" column="0"> |
305 | 26 | <number>0</number> | 26 | <layout class="QVBoxLayout" name="verticalLayout"> |
306 | 27 | </property> | 27 | <property name="spacing"> |
307 | 28 | <item> | 28 | <number>3</number> |
308 | 29 | <layout class="QVBoxLayout" name="verticalLayout_5"> | 29 | </property> |
309 | 30 | <property name="spacing"> | 30 | <item> |
310 | 31 | <number>15</number> | 31 | <widget class="QLabel" name="reset_code"> |
311 | 32 | </property> | 32 | <property name="sizePolicy"> |
312 | 33 | <item> | 33 | <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> |
313 | 34 | <layout class="QVBoxLayout" name="verticalLayout_4"> | 34 | <horstretch>0</horstretch> |
314 | 35 | <property name="spacing"> | 35 | <verstretch>0</verstretch> |
315 | 36 | <number>3</number> | 36 | </sizepolicy> |
316 | 37 | </property> | 37 | </property> |
317 | 38 | <item> | 38 | <property name="minimumSize"> |
318 | 39 | <widget class="QLabel" name="reset_code"> | 39 | <size> |
319 | 40 | <property name="sizePolicy"> | 40 | <width>310</width> |
320 | 41 | <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> | 41 | <height>0</height> |
321 | 42 | <horstretch>0</horstretch> | 42 | </size> |
322 | 43 | <verstretch>0</verstretch> | 43 | </property> |
323 | 44 | </sizepolicy> | 44 | <property name="maximumSize"> |
324 | 45 | </property> | 45 | <size> |
325 | 46 | <property name="minimumSize"> | 46 | <width>16777215</width> |
326 | 47 | <size> | 47 | <height>16777215</height> |
327 | 48 | <width>310</width> | 48 | </size> |
328 | 49 | <height>0</height> | 49 | </property> |
329 | 50 | </size> | 50 | <property name="font"> |
330 | 51 | </property> | 51 | <font> |
331 | 52 | <property name="maximumSize"> | 52 | <bold>true</bold> |
332 | 53 | <size> | 53 | </font> |
333 | 54 | <width>16777215</width> | 54 | </property> |
334 | 55 | <height>16777215</height> | 55 | <property name="text"> |
335 | 56 | </size> | 56 | <string notr="true">reset_code</string> |
336 | 57 | </property> | 57 | </property> |
337 | 58 | <property name="font"> | 58 | </widget> |
338 | 59 | <font> | 59 | </item> |
339 | 60 | <weight>75</weight> | 60 | <item> |
340 | 61 | <bold>true</bold> | 61 | <widget class="QLineEdit" name="reset_code_line_edit"> |
341 | 62 | </font> | 62 | <property name="sizePolicy"> |
342 | 63 | </property> | 63 | <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> |
343 | 64 | <property name="text"> | 64 | <horstretch>0</horstretch> |
344 | 65 | <string notr="true">reset_code</string> | 65 | <verstretch>0</verstretch> |
345 | 66 | </property> | 66 | </sizepolicy> |
346 | 67 | </widget> | 67 | </property> |
347 | 68 | </item> | 68 | <property name="minimumSize"> |
348 | 69 | <item> | 69 | <size> |
349 | 70 | <widget class="QLineEdit" name="reset_code_line_edit"> | 70 | <width>300</width> |
350 | 71 | <property name="sizePolicy"> | 71 | <height>0</height> |
351 | 72 | <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | 72 | </size> |
352 | 73 | <horstretch>0</horstretch> | 73 | </property> |
353 | 74 | <verstretch>0</verstretch> | 74 | <property name="maximumSize"> |
354 | 75 | </sizepolicy> | 75 | <size> |
355 | 76 | </property> | 76 | <width>300</width> |
356 | 77 | <property name="minimumSize"> | 77 | <height>16777215</height> |
357 | 78 | <size> | 78 | </size> |
358 | 79 | <width>300</width> | 79 | </property> |
359 | 80 | <height>0</height> | 80 | </widget> |
360 | 81 | </size> | 81 | </item> |
361 | 82 | </property> | 82 | </layout> |
362 | 83 | <property name="maximumSize"> | 83 | </item> |
363 | 84 | <size> | 84 | <item row="1" column="0"> |
364 | 85 | <width>300</width> | 85 | <layout class="QVBoxLayout" name="verticalLayout_2"> |
365 | 86 | <height>16777215</height> | 86 | <property name="spacing"> |
366 | 87 | </size> | 87 | <number>3</number> |
367 | 88 | </property> | 88 | </property> |
368 | 89 | </widget> | 89 | <item> |
369 | 90 | </item> | 90 | <widget class="QLabel" name="password_label"> |
370 | 91 | </layout> | 91 | <property name="sizePolicy"> |
371 | 92 | </item> | 92 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
372 | 93 | <item> | 93 | <horstretch>0</horstretch> |
373 | 94 | <layout class="QVBoxLayout" name="verticalLayout"> | 94 | <verstretch>0</verstretch> |
374 | 95 | <property name="spacing"> | 95 | </sizepolicy> |
375 | 96 | <number>3</number> | 96 | </property> |
376 | 97 | </property> | 97 | <property name="minimumSize"> |
377 | 98 | <item> | 98 | <size> |
378 | 99 | <widget class="QLabel" name="password_label"> | 99 | <width>310</width> |
379 | 100 | <property name="sizePolicy"> | 100 | <height>0</height> |
380 | 101 | <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> | 101 | </size> |
381 | 102 | <horstretch>0</horstretch> | 102 | </property> |
382 | 103 | <verstretch>0</verstretch> | 103 | <property name="maximumSize"> |
383 | 104 | </sizepolicy> | 104 | <size> |
384 | 105 | </property> | 105 | <width>16777215</width> |
385 | 106 | <property name="minimumSize"> | 106 | <height>16777215</height> |
386 | 107 | <size> | 107 | </size> |
387 | 108 | <width>310</width> | 108 | </property> |
388 | 109 | <height>0</height> | 109 | <property name="font"> |
389 | 110 | </size> | 110 | <font> |
390 | 111 | </property> | 111 | <bold>true</bold> |
391 | 112 | <property name="maximumSize"> | 112 | </font> |
392 | 113 | <size> | 113 | </property> |
393 | 114 | <width>16777215</width> | 114 | <property name="text"> |
394 | 115 | <height>16777215</height> | 115 | <string notr="true">password_label</string> |
395 | 116 | </size> | 116 | </property> |
396 | 117 | </property> | 117 | </widget> |
397 | 118 | <property name="font"> | 118 | </item> |
398 | 119 | <font> | 119 | <item> |
399 | 120 | <weight>75</weight> | 120 | <widget class="QLineEdit" name="password_line_edit"> |
400 | 121 | <bold>true</bold> | 121 | <property name="sizePolicy"> |
401 | 122 | </font> | 122 | <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> |
402 | 123 | </property> | 123 | <horstretch>0</horstretch> |
403 | 124 | <property name="text"> | 124 | <verstretch>0</verstretch> |
404 | 125 | <string notr="true">password_label</string> | 125 | </sizepolicy> |
405 | 126 | </property> | 126 | </property> |
406 | 127 | </widget> | 127 | <property name="minimumSize"> |
407 | 128 | </item> | 128 | <size> |
408 | 129 | <item> | 129 | <width>300</width> |
409 | 130 | <widget class="QLineEdit" name="password_line_edit"> | 130 | <height>0</height> |
410 | 131 | <property name="sizePolicy"> | 131 | </size> |
411 | 132 | <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | 132 | </property> |
412 | 133 | <horstretch>0</horstretch> | 133 | <property name="maximumSize"> |
413 | 134 | <verstretch>0</verstretch> | 134 | <size> |
414 | 135 | </sizepolicy> | 135 | <width>300</width> |
415 | 136 | </property> | 136 | <height>16777215</height> |
416 | 137 | <property name="minimumSize"> | 137 | </size> |
417 | 138 | <size> | 138 | </property> |
418 | 139 | <width>300</width> | 139 | <property name="echoMode"> |
419 | 140 | <height>0</height> | 140 | <enum>QLineEdit::Password</enum> |
420 | 141 | </size> | 141 | </property> |
421 | 142 | </property> | 142 | </widget> |
422 | 143 | <property name="maximumSize"> | 143 | </item> |
423 | 144 | <size> | 144 | </layout> |
424 | 145 | <width>300</width> | 145 | </item> |
425 | 146 | <height>16777215</height> | 146 | <item row="2" column="0"> |
234 | 147 | </size> | ||
235 | 148 | </property> | ||
236 | 149 | <property name="echoMode"> | ||
237 | 150 | <enum>QLineEdit::Password</enum> | ||
238 | 151 | </property> | ||
239 | 152 | </widget> | ||
240 | 153 | </item> | ||
241 | 154 | </layout> | ||
242 | 155 | </item> | ||
243 | 156 | <item> | ||
244 | 157 | <layout class="QVBoxLayout" name="verticalLayout_2"> | ||
245 | 158 | <property name="spacing"> | ||
246 | 159 | <number>3</number> | ||
247 | 160 | </property> | ||
248 | 161 | <item> | ||
249 | 162 | <widget class="QLabel" name="confirm_password_label"> | ||
250 | 163 | <property name="sizePolicy"> | ||
251 | 164 | <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> | ||
252 | 165 | <horstretch>0</horstretch> | ||
253 | 166 | <verstretch>0</verstretch> | ||
254 | 167 | </sizepolicy> | ||
255 | 168 | </property> | ||
256 | 169 | <property name="minimumSize"> | ||
257 | 170 | <size> | ||
258 | 171 | <width>310</width> | ||
259 | 172 | <height>0</height> | ||
260 | 173 | </size> | ||
261 | 174 | </property> | ||
262 | 175 | <property name="font"> | ||
263 | 176 | <font> | ||
264 | 177 | <weight>75</weight> | ||
265 | 178 | <bold>true</bold> | ||
266 | 179 | </font> | ||
267 | 180 | </property> | ||
268 | 181 | <property name="text"> | ||
269 | 182 | <string notr="true">confirm_password_label</string> | ||
270 | 183 | </property> | ||
271 | 184 | </widget> | ||
272 | 185 | </item> | ||
273 | 186 | <item> | ||
274 | 187 | <widget class="QLineEdit" name="confirm_password_line_edit"> | ||
275 | 188 | <property name="sizePolicy"> | ||
276 | 189 | <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | ||
277 | 190 | <horstretch>0</horstretch> | ||
278 | 191 | <verstretch>0</verstretch> | ||
279 | 192 | </sizepolicy> | ||
280 | 193 | </property> | ||
281 | 194 | <property name="minimumSize"> | ||
282 | 195 | <size> | ||
283 | 196 | <width>300</width> | ||
284 | 197 | <height>0</height> | ||
285 | 198 | </size> | ||
286 | 199 | </property> | ||
287 | 200 | <property name="maximumSize"> | ||
288 | 201 | <size> | ||
289 | 202 | <width>300</width> | ||
290 | 203 | <height>16777215</height> | ||
291 | 204 | </size> | ||
292 | 205 | </property> | ||
293 | 206 | <property name="echoMode"> | ||
294 | 207 | <enum>QLineEdit::Password</enum> | ||
295 | 208 | </property> | ||
296 | 209 | </widget> | ||
297 | 210 | </item> | ||
298 | 211 | </layout> | ||
299 | 212 | </item> | ||
300 | 213 | </layout> | ||
301 | 214 | </item> | ||
302 | 215 | <item> | ||
426 | 216 | <layout class="QVBoxLayout" name="verticalLayout_3"> | 147 | <layout class="QVBoxLayout" name="verticalLayout_3"> |
427 | 217 | <property name="spacing"> | 148 | <property name="spacing"> |
429 | 218 | <number>0</number> | 149 | <number>3</number> |
430 | 219 | </property> | 150 | </property> |
431 | 220 | <item> | 151 | <item> |
443 | 221 | <widget class="QLabel" name="password_assistance"> | 152 | <widget class="QLabel" name="confirm_password_label"> |
444 | 222 | <property name="sizePolicy"> | 153 | <property name="sizePolicy"> |
445 | 223 | <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> | 154 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
446 | 224 | <horstretch>0</horstretch> | 155 | <horstretch>0</horstretch> |
447 | 225 | <verstretch>0</verstretch> | 156 | <verstretch>0</verstretch> |
448 | 226 | </sizepolicy> | 157 | </sizepolicy> |
449 | 227 | </property> | 158 | </property> |
450 | 228 | <property name="minimumSize"> | 159 | <property name="minimumSize"> |
451 | 229 | <size> | 160 | <size> |
452 | 230 | <width>220</width> | 161 | <width>310</width> |
453 | 231 | <height>100</height> | 162 | <height>0</height> |
454 | 163 | </size> | ||
455 | 164 | </property> | ||
456 | 165 | <property name="font"> | ||
457 | 166 | <font> | ||
458 | 167 | <bold>true</bold> | ||
459 | 168 | </font> | ||
460 | 169 | </property> | ||
461 | 170 | <property name="text"> | ||
462 | 171 | <string notr="true">confirm_password_label</string> | ||
463 | 172 | </property> | ||
464 | 173 | </widget> | ||
465 | 174 | </item> | ||
466 | 175 | <item> | ||
467 | 176 | <widget class="QLineEdit" name="confirm_password_line_edit"> | ||
468 | 177 | <property name="sizePolicy"> | ||
469 | 178 | <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> | ||
470 | 179 | <horstretch>0</horstretch> | ||
471 | 180 | <verstretch>0</verstretch> | ||
472 | 181 | </sizepolicy> | ||
473 | 182 | </property> | ||
474 | 183 | <property name="minimumSize"> | ||
475 | 184 | <size> | ||
476 | 185 | <width>300</width> | ||
477 | 186 | <height>0</height> | ||
478 | 232 | </size> | 187 | </size> |
479 | 233 | </property> | 188 | </property> |
480 | 234 | <property name="maximumSize"> | 189 | <property name="maximumSize"> |
481 | 235 | <size> | 190 | <size> |
483 | 236 | <width>220</width> | 191 | <width>300</width> |
484 | 237 | <height>16777215</height> | 192 | <height>16777215</height> |
485 | 238 | </size> | 193 | </size> |
486 | 239 | </property> | 194 | </property> |
492 | 240 | <property name="text"> | 195 | <property name="echoMode"> |
493 | 241 | <string notr="true">password_assistance</string> | 196 | <enum>QLineEdit::Password</enum> |
489 | 242 | </property> | ||
490 | 243 | <property name="indent"> | ||
491 | 244 | <number>20</number> | ||
494 | 245 | </property> | 197 | </property> |
495 | 246 | </widget> | 198 | </widget> |
496 | 247 | </item> | 199 | </item> |
497 | 248 | <item> | ||
498 | 249 | <spacer name="verticalSpacer_2"> | ||
499 | 250 | <property name="orientation"> | ||
500 | 251 | <enum>Qt::Vertical</enum> | ||
501 | 252 | </property> | ||
502 | 253 | <property name="sizeHint" stdset="0"> | ||
503 | 254 | <size> | ||
504 | 255 | <width>20</width> | ||
505 | 256 | <height>40</height> | ||
506 | 257 | </size> | ||
507 | 258 | </property> | ||
508 | 259 | </spacer> | ||
509 | 260 | </item> | ||
510 | 261 | <item> | ||
511 | 262 | <spacer name="horizontalSpacer"> | ||
512 | 263 | <property name="orientation"> | ||
513 | 264 | <enum>Qt::Horizontal</enum> | ||
514 | 265 | </property> | ||
515 | 266 | <property name="sizeType"> | ||
516 | 267 | <enum>QSizePolicy::Ignored</enum> | ||
517 | 268 | </property> | ||
518 | 269 | <property name="sizeHint" stdset="0"> | ||
519 | 270 | <size> | ||
520 | 271 | <width>220</width> | ||
521 | 272 | <height>0</height> | ||
522 | 273 | </size> | ||
523 | 274 | </property> | ||
524 | 275 | </spacer> | ||
525 | 276 | </item> | ||
526 | 277 | </layout> | 200 | </layout> |
527 | 278 | </item> | 201 | </item> |
528 | 202 | <item row="1" column="1" rowspan="2"> | ||
529 | 203 | <widget class="QLabel" name="password_assistance"> | ||
530 | 204 | <property name="sizePolicy"> | ||
531 | 205 | <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> | ||
532 | 206 | <horstretch>0</horstretch> | ||
533 | 207 | <verstretch>0</verstretch> | ||
534 | 208 | </sizepolicy> | ||
535 | 209 | </property> | ||
536 | 210 | <property name="minimumSize"> | ||
537 | 211 | <size> | ||
538 | 212 | <width>185</width> | ||
539 | 213 | <height>95</height> | ||
540 | 214 | </size> | ||
541 | 215 | </property> | ||
542 | 216 | <property name="maximumSize"> | ||
543 | 217 | <size> | ||
544 | 218 | <width>185</width> | ||
545 | 219 | <height>95</height> | ||
546 | 220 | </size> | ||
547 | 221 | </property> | ||
548 | 222 | <property name="text"> | ||
549 | 223 | <string notr="true">password_assistance</string> | ||
550 | 224 | </property> | ||
551 | 225 | <property name="wordWrap"> | ||
552 | 226 | <bool>true</bool> | ||
553 | 227 | </property> | ||
554 | 228 | <property name="indent"> | ||
555 | 229 | <number>20</number> | ||
556 | 230 | </property> | ||
557 | 231 | </widget> | ||
558 | 232 | </item> | ||
559 | 279 | </layout> | 233 | </layout> |
560 | 280 | </item> | 234 | </item> |
561 | 281 | <item> | 235 | <item> |
562 | @@ -327,22 +281,5 @@ | |||
563 | 327 | </layout> | 281 | </layout> |
564 | 328 | </widget> | 282 | </widget> |
565 | 329 | <resources/> | 283 | <resources/> |
584 | 330 | <connections> | 284 | <connections/> |
567 | 331 | <connection> | ||
568 | 332 | <sender>confirm_password_line_edit</sender> | ||
569 | 333 | <signal>returnPressed()</signal> | ||
570 | 334 | <receiver>reset_password_button</receiver> | ||
571 | 335 | <slot>click()</slot> | ||
572 | 336 | <hints> | ||
573 | 337 | <hint type="sourcelabel"> | ||
574 | 338 | <x>160</x> | ||
575 | 339 | <y>81</y> | ||
576 | 340 | </hint> | ||
577 | 341 | <hint type="destinationlabel"> | ||
578 | 342 | <x>541</x> | ||
579 | 343 | <y>237</y> | ||
580 | 344 | </hint> | ||
581 | 345 | </hints> | ||
582 | 346 | </connection> | ||
583 | 347 | </connections> | ||
585 | 348 | </ui> | 285 | </ui> |
586 | 349 | 286 | ||
587 | === modified file 'data/qt/resources.qrc' | |||
588 | --- data/qt/resources.qrc 2012-02-16 14:13:36 +0000 | |||
589 | +++ data/qt/resources.qrc 2012-03-20 16:10:09 +0000 | |||
590 | @@ -6,5 +6,7 @@ | |||
591 | 6 | <file>../Ubuntu-B.ttf</file> | 6 | <file>../Ubuntu-B.ttf</file> |
592 | 7 | <file>../balloon_shape.png</file> | 7 | <file>../balloon_shape.png</file> |
593 | 8 | <file>stylesheet.qss</file> | 8 | <file>stylesheet.qss</file> |
594 | 9 | <file>windows.qss</file> | ||
595 | 10 | <file>linux.qss</file> | ||
596 | 9 | </qresource> | 11 | </qresource> |
597 | 10 | </RCC> | 12 | </RCC> |
598 | 11 | 13 | ||
599 | === modified file 'data/qt/setup_account.ui' | |||
600 | --- data/qt/setup_account.ui 2012-03-05 21:52:45 +0000 | |||
601 | +++ data/qt/setup_account.ui 2012-03-20 16:10:09 +0000 | |||
602 | @@ -85,7 +85,6 @@ | |||
603 | 85 | <widget class="QLabel" name="name_label"> | 85 | <widget class="QLabel" name="name_label"> |
604 | 86 | <property name="font"> | 86 | <property name="font"> |
605 | 87 | <font> | 87 | <font> |
606 | 88 | <weight>75</weight> | ||
607 | 89 | <bold>true</bold> | 88 | <bold>true</bold> |
608 | 90 | </font> | 89 | </font> |
609 | 91 | </property> | 90 | </property> |
610 | @@ -114,11 +113,6 @@ | |||
611 | 114 | <height>16777215</height> | 113 | <height>16777215</height> |
612 | 115 | </size> | 114 | </size> |
613 | 116 | </property> | 115 | </property> |
614 | 117 | <property name="font"> | ||
615 | 118 | <font> | ||
616 | 119 | <pointsize>11</pointsize> | ||
617 | 120 | </font> | ||
618 | 121 | </property> | ||
619 | 122 | <property name="formError" stdset="0"> | 116 | <property name="formError" stdset="0"> |
620 | 123 | <bool>false</bool> | 117 | <bool>false</bool> |
621 | 124 | </property> | 118 | </property> |
622 | @@ -135,7 +129,6 @@ | |||
623 | 135 | <widget class="QLabel" name="email_label"> | 129 | <widget class="QLabel" name="email_label"> |
624 | 136 | <property name="font"> | 130 | <property name="font"> |
625 | 137 | <font> | 131 | <font> |
626 | 138 | <weight>75</weight> | ||
627 | 139 | <bold>true</bold> | 132 | <bold>true</bold> |
628 | 140 | </font> | 133 | </font> |
629 | 141 | </property> | 134 | </property> |
630 | @@ -164,11 +157,6 @@ | |||
631 | 164 | <height>16777215</height> | 157 | <height>16777215</height> |
632 | 165 | </size> | 158 | </size> |
633 | 166 | </property> | 159 | </property> |
634 | 167 | <property name="font"> | ||
635 | 168 | <font> | ||
636 | 169 | <pointsize>11</pointsize> | ||
637 | 170 | </font> | ||
638 | 171 | </property> | ||
639 | 172 | <property name="placeholderText"> | 160 | <property name="placeholderText"> |
640 | 173 | <string/> | 161 | <string/> |
641 | 174 | </property> | 162 | </property> |
642 | @@ -229,7 +217,6 @@ | |||
643 | 229 | <widget class="QLabel" name="confirm_email_label"> | 217 | <widget class="QLabel" name="confirm_email_label"> |
644 | 230 | <property name="font"> | 218 | <property name="font"> |
645 | 231 | <font> | 219 | <font> |
646 | 232 | <weight>75</weight> | ||
647 | 233 | <bold>true</bold> | 220 | <bold>true</bold> |
648 | 234 | </font> | 221 | </font> |
649 | 235 | </property> | 222 | </property> |
650 | @@ -258,11 +245,6 @@ | |||
651 | 258 | <height>16777215</height> | 245 | <height>16777215</height> |
652 | 259 | </size> | 246 | </size> |
653 | 260 | </property> | 247 | </property> |
654 | 261 | <property name="font"> | ||
655 | 262 | <font> | ||
656 | 263 | <pointsize>11</pointsize> | ||
657 | 264 | </font> | ||
658 | 265 | </property> | ||
659 | 266 | <property name="placeholderText"> | 248 | <property name="placeholderText"> |
660 | 267 | <string/> | 249 | <string/> |
661 | 268 | </property> | 250 | </property> |
662 | @@ -377,7 +359,6 @@ | |||
663 | 377 | <widget class="QLabel" name="password_label"> | 359 | <widget class="QLabel" name="password_label"> |
664 | 378 | <property name="font"> | 360 | <property name="font"> |
665 | 379 | <font> | 361 | <font> |
666 | 380 | <weight>75</weight> | ||
667 | 381 | <bold>true</bold> | 362 | <bold>true</bold> |
668 | 382 | </font> | 363 | </font> |
669 | 383 | </property> | 364 | </property> |
670 | @@ -406,13 +387,8 @@ | |||
671 | 406 | <height>16777215</height> | 387 | <height>16777215</height> |
672 | 407 | </size> | 388 | </size> |
673 | 408 | </property> | 389 | </property> |
674 | 409 | <property name="font"> | ||
675 | 410 | <font> | ||
676 | 411 | <pointsize>11</pointsize> | ||
677 | 412 | </font> | ||
678 | 413 | </property> | ||
679 | 414 | <property name="toolTip"> | 390 | <property name="toolTip"> |
681 | 415 | <string notr="true">Your password must be at least 8 characters long and at least contain one number and one upper later.</string> | 391 | <string notr="true">Your password must be at least 8 characters long and contain at least one number and one uppercase letter.</string> |
682 | 416 | </property> | 392 | </property> |
683 | 417 | <property name="statusTip"> | 393 | <property name="statusTip"> |
684 | 418 | <string/> | 394 | <string/> |
685 | @@ -477,7 +453,6 @@ | |||
686 | 477 | <widget class="QLabel" name="confirm_password_label"> | 453 | <widget class="QLabel" name="confirm_password_label"> |
687 | 478 | <property name="font"> | 454 | <property name="font"> |
688 | 479 | <font> | 455 | <font> |
689 | 480 | <weight>75</weight> | ||
690 | 481 | <bold>true</bold> | 456 | <bold>true</bold> |
691 | 482 | </font> | 457 | </font> |
692 | 483 | </property> | 458 | </property> |
693 | @@ -506,11 +481,6 @@ | |||
694 | 506 | <height>16777215</height> | 481 | <height>16777215</height> |
695 | 507 | </size> | 482 | </size> |
696 | 508 | </property> | 483 | </property> |
697 | 509 | <property name="font"> | ||
698 | 510 | <font> | ||
699 | 511 | <pointsize>11</pointsize> | ||
700 | 512 | </font> | ||
701 | 513 | </property> | ||
702 | 514 | <property name="echoMode"> | 484 | <property name="echoMode"> |
703 | 515 | <enum>QLineEdit::Password</enum> | 485 | <enum>QLineEdit::Password</enum> |
704 | 516 | </property> | 486 | </property> |
705 | @@ -580,11 +550,6 @@ | |||
706 | 580 | <height>16777215</height> | 550 | <height>16777215</height> |
707 | 581 | </size> | 551 | </size> |
708 | 582 | </property> | 552 | </property> |
709 | 583 | <property name="font"> | ||
710 | 584 | <font> | ||
711 | 585 | <pointsize>11</pointsize> | ||
712 | 586 | </font> | ||
713 | 587 | </property> | ||
714 | 588 | <property name="locale"> | 553 | <property name="locale"> |
715 | 589 | <locale language="English" country="UnitedStates"/> | 554 | <locale language="English" country="UnitedStates"/> |
716 | 590 | </property> | 555 | </property> |
717 | 591 | 556 | ||
718 | === modified file 'data/qt/ssl_dialog.ui' | |||
719 | --- data/qt/ssl_dialog.ui 2012-02-24 15:22:26 +0000 | |||
720 | +++ data/qt/ssl_dialog.ui 2012-03-20 16:10:09 +0000 | |||
721 | @@ -95,11 +95,6 @@ | |||
722 | 95 | </property> | 95 | </property> |
723 | 96 | <item> | 96 | <item> |
724 | 97 | <widget class="QLabel" name="title_label"> | 97 | <widget class="QLabel" name="title_label"> |
725 | 98 | <property name="font"> | ||
726 | 99 | <font> | ||
727 | 100 | <pointsize>14</pointsize> | ||
728 | 101 | </font> | ||
729 | 102 | </property> | ||
730 | 103 | <property name="text"> | 98 | <property name="text"> |
731 | 104 | <string notr="true">Do you want to connect to this server</string> | 99 | <string notr="true">Do you want to connect to this server</string> |
732 | 105 | </property> | 100 | </property> |
733 | 106 | 101 | ||
734 | === modified file 'data/qt/stylesheet.qss' | |||
735 | --- data/qt/stylesheet.qss 2012-03-05 20:30:57 +0000 | |||
736 | +++ data/qt/stylesheet.qss 2012-03-20 16:10:09 +0000 | |||
737 | @@ -1,5 +1,4 @@ | |||
738 | 1 | QWidget { | 1 | QWidget { |
739 | 2 | font-family: "Ubuntu"; | ||
740 | 3 | color: #333333; | 2 | color: #333333; |
741 | 4 | } | 3 | } |
742 | 5 | 4 | ||
743 | @@ -16,7 +15,6 @@ | |||
744 | 16 | 15 | ||
745 | 17 | QLabel#password_assistance { | 16 | QLabel#password_assistance { |
746 | 18 | border-image: url(":/balloon_shape.png"); | 17 | border-image: url(":/balloon_shape.png"); |
747 | 19 | font-size: 12px; | ||
748 | 20 | } | 18 | } |
749 | 21 | 19 | ||
750 | 22 | QLineEdit { | 20 | QLineEdit { |
751 | @@ -91,21 +89,11 @@ | |||
752 | 91 | min-height: 100px; | 89 | min-height: 100px; |
753 | 92 | } | 90 | } |
754 | 93 | 91 | ||
764 | 94 | QFrame#frm_box > QLabel { | 92 | WizardHeader { |
756 | 95 | font-size: 20px; | ||
757 | 96 | } | ||
758 | 97 | |||
759 | 98 | QLabel#title_label { | ||
760 | 99 | font-size: 20px; | ||
761 | 100 | } | ||
762 | 101 | |||
763 | 102 | QFrame#header { | ||
765 | 103 | padding-top: 1px; | 93 | padding-top: 1px; |
766 | 104 | padding-bottom: 1px; | 94 | padding-bottom: 1px; |
767 | 105 | } | 95 | } |
768 | 106 | 96 | ||
769 | 107 | QLabel#form_errors { | 97 | QLabel#form_errors { |
770 | 108 | font: bold 14px; | ||
771 | 109 | color: #df2d1f; | ||
772 | 110 | padding-bottom: 1px; | 98 | padding-bottom: 1px; |
773 | 111 | } | 99 | } |
774 | 112 | 100 | ||
775 | === added file 'data/qt/windows.qss' | |||
776 | --- data/qt/windows.qss 1970-01-01 00:00:00 +0000 | |||
777 | +++ data/qt/windows.qss 2012-03-20 16:10:09 +0000 | |||
778 | @@ -0,0 +1,5 @@ | |||
779 | 1 | /* Styles specific to the windows platform */ | ||
780 | 2 | |||
781 | 3 | QWidget { | ||
782 | 4 | font-family: "Ubuntu"; | ||
783 | 5 | } | ||
784 | 0 | 6 | ||
785 | === modified file 'po/POTFILES.in' | |||
786 | --- po/POTFILES.in 2010-11-19 21:35:11 +0000 | |||
787 | +++ po/POTFILES.in 2012-03-20 16:10:09 +0000 | |||
788 | @@ -1,1 +1,2 @@ | |||
789 | 1 | ubuntu_sso/gtk/gui.py | 1 | ubuntu_sso/gtk/gui.py |
790 | 2 | ubuntu_sso/utils/ui.py | ||
791 | 2 | 3 | ||
792 | === modified file 'run-tests' | |||
793 | --- run-tests 2012-02-17 16:57:34 +0000 | |||
794 | +++ run-tests 2012-03-20 16:10:09 +0000 | |||
795 | @@ -57,7 +57,7 @@ | |||
796 | 57 | 57 | ||
797 | 58 | echo "*** Running QT test suite for ""$MODULE"" ***" | 58 | echo "*** Running QT test suite for ""$MODULE"" ***" |
798 | 59 | ./setup.py build | 59 | ./setup.py build |
800 | 60 | USE_QT_MAINLOOP=True $XVFB_CMDLINE u1trial --reactor=qt4 --gui -p "$GTK_TESTS_PATH" -i "test_windows.py" "$MODULE" | 60 | $XVFB_CMDLINE u1trial --reactor=qt4 --gui -p "$GTK_TESTS_PATH" -i "test_windows.py" "$MODULE" |
801 | 61 | rm -rf _trial_temp | 61 | rm -rf _trial_temp |
802 | 62 | rm -rf build | 62 | rm -rf build |
803 | 63 | 63 | ||
804 | 64 | 64 | ||
805 | === modified file 'ubuntu_sso/__init__.py' | |||
806 | --- ubuntu_sso/__init__.py 2012-02-11 19:25:01 +0000 | |||
807 | +++ ubuntu_sso/__init__.py 2012-03-20 16:10:09 +0000 | |||
808 | @@ -15,6 +15,8 @@ | |||
809 | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
810 | 16 | """Ubuntu Single Sign On client code.""" | 16 | """Ubuntu Single Sign On client code.""" |
811 | 17 | 17 | ||
812 | 18 | import sys | ||
813 | 19 | |||
814 | 18 | # DBus constants | 20 | # DBus constants |
815 | 19 | DBUS_BUS_NAME = "com.ubuntu.sso" | 21 | DBUS_BUS_NAME = "com.ubuntu.sso" |
816 | 20 | 22 | ||
817 | @@ -29,7 +31,13 @@ | |||
818 | 29 | # return codes for UIs | 31 | # return codes for UIs |
819 | 30 | USER_SUCCESS = 0 | 32 | USER_SUCCESS = 0 |
820 | 31 | USER_CANCELLATION = 10 | 33 | USER_CANCELLATION = 10 |
821 | 34 | EXCEPTION_RAISED = 11 | ||
822 | 32 | 35 | ||
823 | 33 | # available UIs | 36 | # available UIs |
824 | 34 | UI_EXECUTABLE_GTK = 'ubuntu-sso-login-gtk' | 37 | UI_EXECUTABLE_GTK = 'ubuntu-sso-login-gtk' |
825 | 35 | UI_EXECUTABLE_QT = 'ubuntu-sso-login-qt' | 38 | UI_EXECUTABLE_QT = 'ubuntu-sso-login-qt' |
826 | 39 | UI_PROXY_CREDS_DIALOG = 'ubuntu-sso-proxy-creds-qt' | ||
827 | 40 | |||
828 | 41 | if getattr(sys, "frozen", None) is not None and sys.platform == "win32": | ||
829 | 42 | UI_EXECUTABLE_QT += ".exe" | ||
830 | 43 | UI_PROXY_CREDS_DIALOG += ".exe" | ||
831 | 36 | 44 | ||
832 | === modified file 'ubuntu_sso/gtk/gui.py' | |||
833 | --- ubuntu_sso/gtk/gui.py 2012-02-17 18:43:17 +0000 | |||
834 | +++ ubuntu_sso/gtk/gui.py 2012-03-20 16:10:09 +0000 | |||
835 | @@ -90,23 +90,6 @@ | |||
836 | 90 | return c | 90 | return c |
837 | 91 | # pylint: enable=C0103 | 91 | # pylint: enable=C0103 |
838 | 92 | 92 | ||
839 | 93 | |||
840 | 94 | # To be removed when Python bindings provide these constants | ||
841 | 95 | # as per http://code.google.com/p/pywebkitgtk/issues/detail?id=44 | ||
842 | 96 | # WebKitLoadStatus | ||
843 | 97 | WEBKIT_LOAD_PROVISIONAL = 0 | ||
844 | 98 | WEBKIT_LOAD_COMMITTED = 1 | ||
845 | 99 | WEBKIT_LOAD_FINISHED = 2 | ||
846 | 100 | WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT = 3 | ||
847 | 101 | WEBKIT_LOAD_FAILED = 4 | ||
848 | 102 | # WebKitWebNavigationReason | ||
849 | 103 | WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED = 0 | ||
850 | 104 | WEBKIT_WEB_NAVIGATION_REASON_FORM_SUBMITTED = 1 | ||
851 | 105 | WEBKIT_WEB_NAVIGATION_REASON_BACK_FORWARD = 2 | ||
852 | 106 | WEBKIT_WEB_NAVIGATION_REASON_RELOAD = 3 | ||
853 | 107 | WEBKIT_WEB_NAVIGATION_REASON_FORM_RESUBMITTED = 4 | ||
854 | 108 | WEBKIT_WEB_NAVIGATION_REASON_OTHER = 5 | ||
855 | 109 | |||
856 | 110 | DEFAULT_WIDTH = 30 | 93 | DEFAULT_WIDTH = 30 |
857 | 111 | # To be replaced by values from the theme (LP: #616526) | 94 | # To be replaced by values from the theme (LP: #616526) |
858 | 112 | HELP_TEXT_COLOR = parse_color("#bfbfbf") | 95 | HELP_TEXT_COLOR = parse_color("#bfbfbf") |
859 | @@ -947,23 +930,14 @@ | |||
860 | 947 | 930 | ||
861 | 948 | self._set_current_page(self.processing_vbox) | 931 | self._set_current_page(self.processing_vbox) |
862 | 949 | 932 | ||
865 | 950 | def on_tc_button_clicked(self, *args, **kwargs): | 933 | def _add_webkit_browser(self): |
866 | 951 | """The T&C button was clicked, create the browser and load terms.""" | 934 | """Add the webkit browser for the t&c.""" |
867 | 952 | # delay the import of webkit to be able to build without it | 935 | # delay the import of webkit to be able to build without it |
868 | 953 | from gi.repository import WebKit # pylint: disable=E0611 | 936 | from gi.repository import WebKit # pylint: disable=E0611 |
869 | 937 | |||
870 | 954 | browser = WebKit.WebView() | 938 | browser = WebKit.WebView() |
871 | 955 | 939 | ||
883 | 956 | # The signal WebKitWebView::load-finished is deprecated and should not | 940 | browser.connect('notify::load-status', |
873 | 957 | # be used in newly-written code. Use the "load-status" property | ||
874 | 958 | # instead. Connect to "notify::load-status" to monitor loading. | ||
875 | 959 | |||
876 | 960 | # nataliabidart (2010-10-04): connecting this signal makes the loading | ||
877 | 961 | # of the Ubuntu One terms URL to fail. So we're using the deprecated | ||
878 | 962 | # 'load-finished' for now. | ||
879 | 963 | |||
880 | 964 | #browser.connect('notify::load-status', | ||
881 | 965 | # self.on_tc_browser_notify_load_status) | ||
882 | 966 | browser.connect('load-finished', | ||
884 | 967 | self.on_tc_browser_notify_load_status) | 941 | self.on_tc_browser_notify_load_status) |
885 | 968 | browser.connect('navigation-policy-decision-requested', | 942 | browser.connect('navigation-policy-decision-requested', |
886 | 969 | self.on_tc_browser_navigation_requested) | 943 | self.on_tc_browser_navigation_requested) |
887 | @@ -978,7 +952,14 @@ | |||
888 | 978 | browser.load_uri(self.tc_url) | 952 | browser.load_uri(self.tc_url) |
889 | 979 | browser.show() | 953 | browser.show() |
890 | 980 | self.tc_browser_window.add(browser) | 954 | self.tc_browser_window.add(browser) |
892 | 981 | self._set_current_page(self.processing_vbox) | 955 | |
893 | 956 | def on_tc_button_clicked(self, *args, **kwargs): | ||
894 | 957 | """The T&C button was clicked, create the browser and load terms.""" | ||
895 | 958 | if self.tc_browser_window.get_child() is None: | ||
896 | 959 | self._add_webkit_browser() | ||
897 | 960 | self._set_current_page(self.processing_vbox) | ||
898 | 961 | else: | ||
899 | 962 | self._set_current_page(self.tc_browser_vbox) | ||
900 | 982 | 963 | ||
901 | 983 | def on_tc_back_button_clicked(self, *args, **kwargs): | 964 | def on_tc_back_button_clicked(self, *args, **kwargs): |
902 | 984 | """T & C 'back' button was clicked, return to the previous page.""" | 965 | """T & C 'back' button was clicked, return to the previous page.""" |
903 | @@ -986,14 +967,18 @@ | |||
904 | 986 | 967 | ||
905 | 987 | def on_tc_browser_notify_load_status(self, browser, *args, **kwargs): | 968 | def on_tc_browser_notify_load_status(self, browser, *args, **kwargs): |
906 | 988 | """The T&C page is being loaded.""" | 969 | """The T&C page is being loaded.""" |
908 | 989 | if browser.get_load_status() == WEBKIT_LOAD_FINISHED: | 970 | from gi.repository import WebKit # pylint: disable=E0611 |
909 | 971 | |||
910 | 972 | if browser.get_load_status().real == WebKit.LoadStatus.FINISHED: | ||
911 | 990 | self._set_current_page(self.tc_browser_vbox) | 973 | self._set_current_page(self.tc_browser_vbox) |
912 | 991 | 974 | ||
913 | 992 | def on_tc_browser_navigation_requested(self, browser, frame, request, | 975 | def on_tc_browser_navigation_requested(self, browser, frame, request, |
914 | 993 | action, decision, *args, **kwargs): | 976 | action, decision, *args, **kwargs): |
915 | 994 | """The user wants to navigate within the T&C browser.""" | 977 | """The user wants to navigate within the T&C browser.""" |
916 | 978 | from gi.repository import WebKit # pylint: disable=E0611 | ||
917 | 979 | |||
918 | 995 | if action is not None and \ | 980 | if action is not None and \ |
920 | 996 | action.get_reason() == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED: | 981 | action.get_reason() == WebKit.WebNavigationReason.LINK_CLICKED: |
921 | 997 | if decision is not None: | 982 | if decision is not None: |
922 | 998 | decision.ignore() | 983 | decision.ignore() |
923 | 999 | url = action.get_original_uri() | 984 | url = action.get_original_uri() |
924 | 1000 | 985 | ||
925 | === modified file 'ubuntu_sso/gtk/tests/test_gui.py' | |||
926 | --- ubuntu_sso/gtk/tests/test_gui.py 2012-02-17 18:43:17 +0000 | |||
927 | +++ ubuntu_sso/gtk/tests/test_gui.py 2012-03-20 16:10:09 +0000 | |||
928 | @@ -134,7 +134,7 @@ | |||
929 | 134 | 134 | ||
930 | 135 | def get_load_status(self): | 135 | def get_load_status(self): |
931 | 136 | """Return the current load status.""" | 136 | """Return the current load status.""" |
933 | 137 | return gui.WEBKIT_LOAD_FINISHED | 137 | return WebKit.LoadStatus.FINISHED |
934 | 138 | 138 | ||
935 | 139 | def show(self): | 139 | def show(self): |
936 | 140 | """Show this instance.""" | 140 | """Show this instance.""" |
937 | @@ -981,7 +981,7 @@ | |||
938 | 981 | def test_notify_load_finished_connected(self): | 981 | def test_notify_load_finished_connected(self): |
939 | 982 | """The 'load-finished' signal is connected.""" | 982 | """The 'load-finished' signal is connected.""" |
940 | 983 | expected = [self.ui.on_tc_browser_notify_load_status] | 983 | expected = [self.ui.on_tc_browser_notify_load_status] |
942 | 984 | self.assertEqual(self.browser._signals['load-finished'], | 984 | self.assertEqual(self.browser._signals['notify::load-status'], |
943 | 985 | expected) | 985 | expected) |
944 | 986 | 986 | ||
945 | 987 | def test_tc_loaded_morphs_into_tc_browser_vbox(self): | 987 | def test_tc_loaded_morphs_into_tc_browser_vbox(self): |
946 | @@ -998,7 +998,7 @@ | |||
947 | 998 | def test_navigation_requested_succeeds_for_no_clicking(self): | 998 | def test_navigation_requested_succeeds_for_no_clicking(self): |
948 | 999 | """The navigation request succeeds when user hasn't clicked a link.""" | 999 | """The navigation request succeeds when user hasn't clicked a link.""" |
949 | 1000 | action = WebKit.WebNavigationAction() | 1000 | action = WebKit.WebNavigationAction() |
951 | 1001 | action.set_reason(gui.WEBKIT_WEB_NAVIGATION_REASON_OTHER) | 1001 | action.set_reason(WebKit.WebNavigationReason.OTHER) |
952 | 1002 | 1002 | ||
953 | 1003 | decision = WebKit.WebPolicyDecision() | 1003 | decision = WebKit.WebPolicyDecision() |
954 | 1004 | decision.use = self._set_called | 1004 | decision.use = self._set_called |
955 | @@ -1011,7 +1011,7 @@ | |||
956 | 1011 | def test_navigation_requested_ignores_clicked_links(self): | 1011 | def test_navigation_requested_ignores_clicked_links(self): |
957 | 1012 | """The navigation request is ignored if a link was clicked.""" | 1012 | """The navigation request is ignored if a link was clicked.""" |
958 | 1013 | action = WebKit.WebNavigationAction() | 1013 | action = WebKit.WebNavigationAction() |
960 | 1014 | action.set_reason(gui.WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) | 1014 | action.set_reason(WebKit.WebNavigationReason.LINK_CLICKED) |
961 | 1015 | 1015 | ||
962 | 1016 | decision = WebKit.WebPolicyDecision() | 1016 | decision = WebKit.WebPolicyDecision() |
963 | 1017 | decision.ignore = self._set_called | 1017 | decision.ignore = self._set_called |
964 | @@ -1037,7 +1037,7 @@ | |||
965 | 1037 | """ | 1037 | """ |
966 | 1038 | url = 'http://something.com/yadda' | 1038 | url = 'http://something.com/yadda' |
967 | 1039 | action = WebKit.WebNavigationAction() | 1039 | action = WebKit.WebNavigationAction() |
969 | 1040 | action.set_reason(gui.WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) | 1040 | action.set_reason(WebKit.WebNavigationReason.LINK_CLICKED) |
970 | 1041 | action.set_original_uri(url) | 1041 | action.set_original_uri(url) |
971 | 1042 | 1042 | ||
972 | 1043 | decision = WebKit.WebPolicyDecision() | 1043 | decision = WebKit.WebPolicyDecision() |
973 | @@ -1050,6 +1050,35 @@ | |||
974 | 1050 | self.ui.on_tc_browser_navigation_requested(**kwargs) | 1050 | self.ui.on_tc_browser_navigation_requested(**kwargs) |
975 | 1051 | self.assertEqual(self._called, ((url,), {})) | 1051 | self.assertEqual(self._called, ((url,), {})) |
976 | 1052 | 1052 | ||
977 | 1053 | def test_on_tc_button_clicked_no_child(self): | ||
978 | 1054 | """Test the tc loading with no child.""" | ||
979 | 1055 | called = [] | ||
980 | 1056 | |||
981 | 1057 | def fake_add_browser(): | ||
982 | 1058 | """Fake add browser.""" | ||
983 | 1059 | called.append('fake_add_browser') | ||
984 | 1060 | |||
985 | 1061 | self.patch(self.ui, '_add_webkit_browser', fake_add_browser) | ||
986 | 1062 | self.patch(self.ui.tc_browser_window, 'get_child', lambda: None) | ||
987 | 1063 | |||
988 | 1064 | self.ui.on_tc_button_clicked() | ||
989 | 1065 | self.assertIn('fake_add_browser', called) | ||
990 | 1066 | |||
991 | 1067 | def test_on_tc_button_clicked_child(self): | ||
992 | 1068 | """Test the tc loading with child.""" | ||
993 | 1069 | called = [] | ||
994 | 1070 | |||
995 | 1071 | def fake_add_browser(i_self): | ||
996 | 1072 | """Fake add browser.""" | ||
997 | 1073 | called.append('fake_add_browser') | ||
998 | 1074 | |||
999 | 1075 | self.patch(self.ui, '_add_webkit_browser', fake_add_browser) | ||
1000 | 1076 | |||
1001 | 1077 | browser = WebKit.WebView() | ||
1002 | 1078 | self.ui.tc_browser_window.add(browser) | ||
1003 | 1079 | self.ui.on_tc_button_clicked() | ||
1004 | 1080 | self.assertNotIn('fake_add_browser', called) | ||
1005 | 1081 | |||
1006 | 1053 | 1082 | ||
1007 | 1054 | class RegistrationErrorTestCase(UbuntuSSOClientTestCase): | 1083 | class RegistrationErrorTestCase(UbuntuSSOClientTestCase): |
1008 | 1055 | """Test suite for the user registration error handling.""" | 1084 | """Test suite for the user registration error handling.""" |
1009 | 1056 | 1085 | ||
1010 | === modified file 'ubuntu_sso/qt/__init__.py' | |||
1011 | --- ubuntu_sso/qt/__init__.py 2012-03-05 18:56:50 +0000 | |||
1012 | +++ ubuntu_sso/qt/__init__.py 2012-03-20 16:10:09 +0000 | |||
1013 | @@ -18,19 +18,27 @@ | |||
1014 | 18 | 18 | ||
1015 | 19 | import collections | 19 | import collections |
1016 | 20 | 20 | ||
1019 | 21 | 21 | from PyQt4 import QtGui, QtCore | |
1020 | 22 | LINK_STYLE = ('<a href="{link_url}">' | 22 | |
1021 | 23 | from ubuntu_sso.logger import setup_gui_logging | ||
1022 | 24 | from ubuntu_sso.utils.ui import GENERIC_BACKEND_ERROR | ||
1023 | 25 | |||
1024 | 26 | logger = setup_gui_logging('ubuntu_sso.qt') | ||
1025 | 27 | |||
1026 | 28 | LINK_STYLE = (u'<a href="{link_url}">' | ||
1027 | 23 | '<span style="color:#df2d1f;">{link_text}</span></a>') | 29 | '<span style="color:#df2d1f;">{link_text}</span></a>') |
1028 | 24 | ERROR_ALL = '__all__' | 30 | ERROR_ALL = '__all__' |
1030 | 25 | ERROR_STYLE = u'<font color="#df2d1f"><b>%s</b></font>' | 31 | ERROR_STYLE = u'<font color="#df2d1f" style="font-size:small"><b>%s</b></font>' |
1031 | 26 | ERROR_MESSAGE = 'message' | 32 | ERROR_MESSAGE = 'message' |
1032 | 27 | PREFERED_UI_SIZE = {'width': 550, 'height': 525} | 33 | PREFERED_UI_SIZE = {'width': 550, 'height': 525} |
1034 | 28 | TITLE_STYLE = u'<span style="font-size:24px">%s</span>' | 34 | TITLE_STYLE = u'<span style="font-size:xx-large;font-weight:bold;">%s</span>' |
1035 | 35 | WINDOW_TITLE = 'Ubuntu Single Sign On' | ||
1036 | 29 | 36 | ||
1037 | 30 | 37 | ||
1038 | 31 | # Based on the gtk implementation | 38 | # Based on the gtk implementation |
1039 | 32 | def build_general_error_message(errordict): | 39 | def build_general_error_message(errordict): |
1040 | 33 | """Build a user-friendly error message from the errordict.""" | 40 | """Build a user-friendly error message from the errordict.""" |
1041 | 41 | logger.debug('build_general_error_message: errordict is: %r.', errordict) | ||
1042 | 34 | result = '' | 42 | result = '' |
1043 | 35 | if isinstance(errordict, collections.Mapping): | 43 | if isinstance(errordict, collections.Mapping): |
1044 | 36 | msg1 = errordict.get(ERROR_ALL) | 44 | msg1 = errordict.get(ERROR_ALL) |
1045 | @@ -50,5 +58,25 @@ | |||
1046 | 50 | result = '\n'.join( | 58 | result = '\n'.join( |
1047 | 51 | [('%s: %s' % (k, v)) for k, v in errordict.iteritems()]) | 59 | [('%s: %s' % (k, v)) for k, v in errordict.iteritems()]) |
1048 | 52 | else: | 60 | else: |
1050 | 53 | result = repr(errordict) | 61 | result = GENERIC_BACKEND_ERROR |
1051 | 62 | logger.error('build_general_error_message with unknown error: %r', | ||
1052 | 63 | errordict) | ||
1053 | 64 | |||
1054 | 65 | logger.info('build_general_error_message: returning %r.', result) | ||
1055 | 54 | return result | 66 | return result |
1056 | 67 | |||
1057 | 68 | |||
1058 | 69 | def maybe_elide_text(label, text, width, markup=None): | ||
1059 | 70 | """Set 'text' to be the 'label's text. | ||
1060 | 71 | |||
1061 | 72 | If 'text' is longer than 'width', set the label's tooltip to be the full | ||
1062 | 73 | text, and the text itself to be the elided version of 'text'. | ||
1063 | 74 | |||
1064 | 75 | """ | ||
1065 | 76 | fm = QtGui.QFontMetrics(label.font()) | ||
1066 | 77 | elided_text = fm.elidedText(text, QtCore.Qt.ElideRight, width) | ||
1067 | 78 | if elided_text != text: | ||
1068 | 79 | label.setToolTip(text) | ||
1069 | 80 | if markup is not None: | ||
1070 | 81 | elided_text = markup % elided_text | ||
1071 | 82 | label.setText(elided_text) | ||
1072 | 55 | 83 | ||
1073 | === modified file 'ubuntu_sso/qt/common.py' | |||
1074 | --- ubuntu_sso/qt/common.py 2012-02-16 14:13:36 +0000 | |||
1075 | +++ ubuntu_sso/qt/common.py 2012-03-20 16:10:09 +0000 | |||
1076 | @@ -28,9 +28,9 @@ | |||
1077 | 28 | ) | 28 | ) |
1078 | 29 | 29 | ||
1079 | 30 | # all the text + styles that are used in the gui | 30 | # all the text + styles that are used in the gui |
1083 | 31 | BAD = u'<img src=":/password_hint_warning.png" /><font> %s </font>' | 31 | BAD = u'<img src=":/password_hint_warning.png" /><small> %s </small>' |
1084 | 32 | GOOD = u'<img src=":/password_hint_ok.png" /><font> %s </font>' | 32 | GOOD = u'<img src=":/password_hint_ok.png" /><small> %s </small>' |
1085 | 33 | NORMAL = u'<font> %s </font>' | 33 | NORMAL = u'<small> %s </small>' |
1086 | 34 | 34 | ||
1087 | 35 | 35 | ||
1088 | 36 | def password_assistance(line_edit, assistance, icon_type=BAD): | 36 | def password_assistance(line_edit, assistance, icon_type=BAD): |
1089 | 37 | 37 | ||
1090 | === modified file 'ubuntu_sso/qt/current_user_sign_in_page.py' | |||
1091 | --- ubuntu_sso/qt/current_user_sign_in_page.py 2012-03-05 20:30:57 +0000 | |||
1092 | +++ ubuntu_sso/qt/current_user_sign_in_page.py 2012-03-20 16:10:09 +0000 | |||
1093 | @@ -61,6 +61,11 @@ | |||
1094 | 61 | } | 61 | } |
1095 | 62 | return result | 62 | return result |
1096 | 63 | 63 | ||
1097 | 64 | @property | ||
1098 | 65 | def password(self): | ||
1099 | 66 | """Return the content of the password edit.""" | ||
1100 | 67 | return unicode(self.ui.password_edit.text()) | ||
1101 | 68 | |||
1102 | 64 | def on_user_not_validated(self, app_name, email): | 69 | def on_user_not_validated(self, app_name, email): |
1103 | 65 | """Show the validate email page.""" | 70 | """Show the validate email page.""" |
1104 | 66 | self.hide_overlay() | 71 | self.hide_overlay() |
1105 | @@ -76,6 +81,7 @@ | |||
1106 | 76 | 81 | ||
1107 | 77 | def initializePage(self): | 82 | def initializePage(self): |
1108 | 78 | """Setup UI details.""" | 83 | """Setup UI details.""" |
1109 | 84 | logger.debug('initializePage - About to show CurrentUserSignInPage') | ||
1110 | 79 | self.setButtonText(QtGui.QWizard.CancelButton, CANCEL_BUTTON) | 85 | self.setButtonText(QtGui.QWizard.CancelButton, CANCEL_BUTTON) |
1111 | 80 | # Layout without custom button 1, | 86 | # Layout without custom button 1, |
1112 | 81 | # without finish button | 87 | # without finish button |
1113 | @@ -94,10 +100,8 @@ | |||
1114 | 94 | 100 | ||
1115 | 95 | def _set_translated_strings(self): | 101 | def _set_translated_strings(self): |
1116 | 96 | """Set the translated strings.""" | 102 | """Set the translated strings.""" |
1117 | 97 | logger.debug('CurrentUserSignInPage._set_translated_strings') | ||
1118 | 98 | self.setTitle(LOGIN_TITLE.format(app_name=self.app_name)) | 103 | self.setTitle(LOGIN_TITLE.format(app_name=self.app_name)) |
1119 | 99 | self.setSubTitle(LOGIN_SUBTITLE % {'app_name': self.app_name}) | 104 | self.setSubTitle(LOGIN_SUBTITLE % {'app_name': self.app_name}) |
1120 | 100 | |||
1121 | 101 | self.ui.email_label.setText(EMAIL_LABEL) | 105 | self.ui.email_label.setText(EMAIL_LABEL) |
1122 | 102 | self.ui.password_label.setText(LOGIN_PASSWORD_LABEL) | 106 | self.ui.password_label.setText(LOGIN_PASSWORD_LABEL) |
1123 | 103 | forgotten_text = LINK_STYLE.format(link_url='#', | 107 | forgotten_text = LINK_STYLE.format(link_url='#', |
1124 | @@ -107,7 +111,6 @@ | |||
1125 | 107 | 111 | ||
1126 | 108 | def _connect_ui(self): | 112 | def _connect_ui(self): |
1127 | 109 | """Connect the buttons to perform actions.""" | 113 | """Connect the buttons to perform actions.""" |
1128 | 110 | logger.debug('CurrentUserSignInPage._connect_buttons') | ||
1129 | 111 | self.ui.forgot_password_label.linkActivated.connect( | 114 | self.ui.forgot_password_label.linkActivated.connect( |
1130 | 112 | self.on_forgotten_password) | 115 | self.on_forgotten_password) |
1131 | 113 | self.ui.email_edit.textChanged.connect(self._validate) | 116 | self.ui.email_edit.textChanged.connect(self._validate) |
1132 | @@ -116,18 +119,16 @@ | |||
1133 | 116 | 119 | ||
1134 | 117 | def _validate(self): | 120 | def _validate(self): |
1135 | 118 | """Perform input validation.""" | 121 | """Perform input validation.""" |
1136 | 119 | valid = True | ||
1137 | 120 | correct_mail = is_correct_email(unicode(self.ui.email_edit.text())) | 122 | correct_mail = is_correct_email(unicode(self.ui.email_edit.text())) |
1142 | 121 | password = unicode(self.ui.password_edit.text()) | 123 | correct_password = len(unicode(self.ui.password_edit.text())) > 0 |
1143 | 122 | if not correct_mail or not password: | 124 | enabled = correct_mail and correct_password |
1144 | 123 | valid = False | 125 | self.ui.sign_in_button.setEnabled(enabled) |
1141 | 124 | self.ui.sign_in_button.setEnabled(valid) | ||
1145 | 125 | 126 | ||
1146 | 126 | def login(self): | 127 | def login(self): |
1147 | 127 | """Perform the login using the self.backend.""" | 128 | """Perform the login using the self.backend.""" |
1148 | 128 | logger.debug('CurrentUserSignInPage.login') | ||
1149 | 129 | # grab the data from the view and call the backend | 129 | # grab the data from the view and call the backend |
1150 | 130 | email = unicode(self.ui.email_edit.text()) | 130 | email = unicode(self.ui.email_edit.text()) |
1151 | 131 | logger.info('CurrentUserSignInPage.login for: %s', email) | ||
1152 | 131 | password = unicode(self.ui.password_edit.text()) | 132 | password = unicode(self.ui.password_edit.text()) |
1153 | 132 | args = (self.app_name, email, password) | 133 | args = (self.app_name, email, password) |
1154 | 133 | if self.ping_url: | 134 | if self.ping_url: |
1155 | @@ -146,18 +147,18 @@ | |||
1156 | 146 | # let the user know | 147 | # let the user know |
1157 | 147 | logger.error('Got error when login %s, error: %s', | 148 | logger.error('Got error when login %s, error: %s', |
1158 | 148 | self.app_name, error) | 149 | self.app_name, error) |
1160 | 149 | self.show_error(self.app_name, build_general_error_message(error)) | 150 | self.show_error(build_general_error_message(error)) |
1161 | 150 | 151 | ||
1162 | 151 | def on_logged_in(self, app_name, result): | 152 | def on_logged_in(self, app_name, result): |
1163 | 152 | """We managed to log in.""" | 153 | """We managed to log in.""" |
1164 | 153 | logger.info('Logged in for %s', app_name) | 154 | logger.info('Logged in for %s', app_name) |
1165 | 154 | self.hide_overlay() | 155 | self.hide_overlay() |
1166 | 155 | email = unicode(self.ui.email_edit.text()) | 156 | email = unicode(self.ui.email_edit.text()) |
1167 | 157 | logger.debug('About to emit userLoggedIn signal with: (%s).', email) | ||
1168 | 156 | self.userLoggedIn.emit(email) | 158 | self.userLoggedIn.emit(email) |
1169 | 157 | logger.debug('Wizard.loginSuccess emitted.') | ||
1170 | 158 | 159 | ||
1171 | 159 | def on_forgotten_password(self, link=None): | 160 | def on_forgotten_password(self, link=None): |
1172 | 160 | """Show the user the forgotten password page.""" | 161 | """Show the user the forgotten password page.""" |
1173 | 161 | logger.info('Forgotten password') | ||
1174 | 162 | self.hide_overlay() | 162 | self.hide_overlay() |
1175 | 163 | logger.debug('About to emit passwordForgotten signal') | ||
1176 | 163 | self.passwordForgotten.emit() | 164 | self.passwordForgotten.emit() |
1177 | 164 | 165 | ||
1178 | === modified file 'ubuntu_sso/qt/email_verification_page.py' | |||
1179 | --- ubuntu_sso/qt/email_verification_page.py 2012-03-05 20:30:57 +0000 | |||
1180 | +++ ubuntu_sso/qt/email_verification_page.py 2012-03-20 16:10:09 +0000 | |||
1181 | @@ -71,7 +71,6 @@ | |||
1182 | 71 | 71 | ||
1183 | 72 | def _connect_ui(self): | 72 | def _connect_ui(self): |
1184 | 73 | """Set the connection of signals.""" | 73 | """Set the connection of signals.""" |
1185 | 74 | logger.debug('EmailVerificationController._connect_ui') | ||
1186 | 75 | self.ui.verification_code_edit.textChanged.connect( | 74 | self.ui.verification_code_edit.textChanged.connect( |
1187 | 76 | self.validate_form) | 75 | self.validate_form) |
1188 | 77 | self.next_button.clicked.connect(self.validate_email) | 76 | self.next_button.clicked.connect(self.validate_email) |
1189 | @@ -84,7 +83,6 @@ | |||
1190 | 84 | 83 | ||
1191 | 85 | def _set_translated_strings(self): | 84 | def _set_translated_strings(self): |
1192 | 86 | """Set the different titles.""" | 85 | """Set the different titles.""" |
1193 | 87 | logger.debug('EmailVerificationController._set_titles') | ||
1194 | 88 | self.header.set_title(VERIFY_EMAIL_TITLE) | 86 | self.header.set_title(VERIFY_EMAIL_TITLE) |
1195 | 89 | self.header.set_subtitle(VERIFY_EMAIL_CONTENT % { | 87 | self.header.set_subtitle(VERIFY_EMAIL_CONTENT % { |
1196 | 90 | "app_name": self.app_name, | 88 | "app_name": self.app_name, |
1197 | @@ -103,7 +101,8 @@ | |||
1198 | 103 | 101 | ||
1199 | 104 | def validate_email(self): | 102 | def validate_email(self): |
1200 | 105 | """Call the next action.""" | 103 | """Call the next action.""" |
1202 | 106 | logger.debug('EmailVerificationController.validate_email') | 104 | logger.debug('EmailVerificationController.validate_email for: %s', |
1203 | 105 | self.email) | ||
1204 | 107 | code = unicode(self.ui.verification_code_edit.text()) | 106 | code = unicode(self.ui.verification_code_edit.text()) |
1205 | 108 | args = (self.app_name, self.email, self.password, code) | 107 | args = (self.app_name, self.email, self.password, code) |
1206 | 109 | self.hide_error() | 108 | self.hide_error() |
1207 | @@ -123,21 +122,25 @@ | |||
1208 | 123 | 122 | ||
1209 | 124 | def on_email_validated(self, app_name, email): | 123 | def on_email_validated(self, app_name, email): |
1210 | 125 | """Signal thrown after the email is validated.""" | 124 | """Signal thrown after the email is validated.""" |
1212 | 126 | logger.info('EmailVerificationController.on_email_validated') | 125 | logger.info('EmailVerificationController.on_email_validated for %s, ' |
1213 | 126 | 'email: %s', app_name, email) | ||
1214 | 127 | self.hide_overlay() | 127 | self.hide_overlay() |
1215 | 128 | self.registrationSuccess.emit(self.email) | 128 | self.registrationSuccess.emit(self.email) |
1216 | 129 | 129 | ||
1217 | 130 | def on_email_validation_error(self, app_name, error): | 130 | def on_email_validation_error(self, app_name, error): |
1218 | 131 | """Signal thrown when there's a problem validating the email.""" | 131 | """Signal thrown when there's a problem validating the email.""" |
1219 | 132 | logger.error('Got error on email validation %s, error: %s', | ||
1220 | 133 | app_name, error) | ||
1221 | 132 | self.hide_overlay() | 134 | self.hide_overlay() |
1222 | 133 | msg = error.pop(ERROR_EMAIL_TOKEN, '') | 135 | msg = error.pop(ERROR_EMAIL_TOKEN, '') |
1223 | 134 | msg += build_general_error_message(error) | 136 | msg += build_general_error_message(error) |
1225 | 135 | self.show_error(self.app_name, msg) | 137 | self.show_error(msg) |
1226 | 136 | 138 | ||
1227 | 137 | # pylint: disable=C0103 | 139 | # pylint: disable=C0103 |
1228 | 138 | 140 | ||
1229 | 139 | def initializePage(self): | 141 | def initializePage(self): |
1230 | 140 | """Called to prepare the page just before it is shown.""" | 142 | """Called to prepare the page just before it is shown.""" |
1231 | 143 | logger.debug('initializePage - About to show EmailVerificationPage') | ||
1232 | 141 | self.next_button.setDefault(True) | 144 | self.next_button.setDefault(True) |
1233 | 142 | self.next_button.setEnabled(False) | 145 | self.next_button.setEnabled(False) |
1234 | 143 | self.wizard().setButtonLayout([QtGui.QWizard.Stretch]) | 146 | self.wizard().setButtonLayout([QtGui.QWizard.Stretch]) |
1235 | 144 | 147 | ||
1236 | === modified file 'ubuntu_sso/qt/enhanced_check_box.py' | |||
1237 | --- ubuntu_sso/qt/enhanced_check_box.py 2012-03-01 16:53:29 +0000 | |||
1238 | +++ ubuntu_sso/qt/enhanced_check_box.py 2012-03-20 16:10:09 +0000 | |||
1239 | @@ -24,11 +24,12 @@ | |||
1240 | 24 | class EnhancedCheckBox(QtGui.QCheckBox): | 24 | class EnhancedCheckBox(QtGui.QCheckBox): |
1241 | 25 | """Enhanced QCheckBox to support links in the message displayed.""" | 25 | """Enhanced QCheckBox to support links in the message displayed.""" |
1242 | 26 | 26 | ||
1245 | 27 | def __init__(self, text=""): | 27 | def __init__(self, text="", parent=None): |
1246 | 28 | QtGui.QCheckBox.__init__(self) | 28 | QtGui.QCheckBox.__init__(self, parent) |
1247 | 29 | hbox = QtGui.QHBoxLayout() | 29 | hbox = QtGui.QHBoxLayout() |
1248 | 30 | hbox.setAlignment(QtCore.Qt.AlignLeft) | ||
1249 | 30 | self.text_label = QtGui.QLabel(text) | 31 | self.text_label = QtGui.QLabel(text) |
1251 | 31 | self.text_label.setAlignment(QtCore.Qt.AlignTop) | 32 | self.text_label.setWordWrap(True) |
1252 | 32 | self.text_label.setOpenExternalLinks(True) | 33 | self.text_label.setOpenExternalLinks(True) |
1253 | 33 | padding = self.iconSize().width() | 34 | padding = self.iconSize().width() |
1254 | 34 | self.text_label.setStyleSheet("margin-top: -3px;" | 35 | self.text_label.setStyleSheet("margin-top: -3px;" |
1255 | @@ -37,6 +38,11 @@ | |||
1256 | 37 | hbox.addWidget(self.text_label) | 38 | hbox.addWidget(self.text_label) |
1257 | 38 | self.setLayout(hbox) | 39 | self.setLayout(hbox) |
1258 | 39 | 40 | ||
1259 | 41 | if parent is not None: | ||
1260 | 42 | lines = self.text_label.width() / float(parent.width()) | ||
1261 | 43 | self.text_label.setMinimumWidth(parent.width()) | ||
1262 | 44 | self.setMinimumHeight(self.height() * lines) | ||
1263 | 45 | |||
1264 | 40 | self.stateChanged.connect(self.text_label.setFocus) | 46 | self.stateChanged.connect(self.text_label.setFocus) |
1265 | 41 | 47 | ||
1266 | 42 | def text(self): | 48 | def text(self): |
1267 | 43 | 49 | ||
1268 | === modified file 'ubuntu_sso/qt/forgotten_password_page.py' | |||
1269 | --- ubuntu_sso/qt/forgotten_password_page.py 2012-03-05 20:31:22 +0000 | |||
1270 | +++ ubuntu_sso/qt/forgotten_password_page.py 2012-03-20 16:10:09 +0000 | |||
1271 | @@ -63,6 +63,7 @@ | |||
1272 | 63 | 63 | ||
1273 | 64 | def initializePage(self): | 64 | def initializePage(self): |
1274 | 65 | """Set the initial state of ForgottenPassword page.""" | 65 | """Set the initial state of ForgottenPassword page.""" |
1275 | 66 | logger.debug('initializePage - About to show ForgottenPasswordPage') | ||
1276 | 66 | self.ui.send_button.setDefault(True) | 67 | self.ui.send_button.setDefault(True) |
1277 | 67 | enabled = not self.ui.email_line_edit.text().isEmpty() | 68 | enabled = not self.ui.email_line_edit.text().isEmpty() |
1278 | 68 | self.ui.send_button.setEnabled(enabled) | 69 | self.ui.send_button.setEnabled(enabled) |
1279 | @@ -98,6 +99,7 @@ | |||
1280 | 98 | """Send the request password operation.""" | 99 | """Send the request password operation.""" |
1281 | 99 | self.hide_error() | 100 | self.hide_error() |
1282 | 100 | args = (self.app_name, self.email_address) | 101 | args = (self.app_name, self.email_address) |
1283 | 102 | logger.debug('Sending request new password for %s, email: %s', *args) | ||
1284 | 101 | f = self.backend.request_password_reset_token | 103 | f = self.backend.request_password_reset_token |
1285 | 102 | 104 | ||
1286 | 103 | error_handler = partial(self._handle_error, f, | 105 | error_handler = partial(self._handle_error, f, |
1287 | @@ -113,6 +115,8 @@ | |||
1288 | 113 | 115 | ||
1289 | 114 | def on_password_reset_token_sent(self, app_name, email): | 116 | def on_password_reset_token_sent(self, app_name, email): |
1290 | 115 | """Action taken when we managed to get the password reset done.""" | 117 | """Action taken when we managed to get the password reset done.""" |
1291 | 118 | logger.info('ForgottenPasswordPage.on_password_reset_token_sent for ' | ||
1292 | 119 | '%s, email: %s', app_name, email) | ||
1293 | 116 | # ignore the result and move to the reset page | 120 | # ignore the result and move to the reset page |
1294 | 117 | self.hide_overlay() | 121 | self.hide_overlay() |
1295 | 118 | self.passwordResetTokenSent.emit(email) | 122 | self.passwordResetTokenSent.emit(email) |
1296 | @@ -123,4 +127,4 @@ | |||
1297 | 123 | # set the error message | 127 | # set the error message |
1298 | 124 | self.hide_overlay() | 128 | self.hide_overlay() |
1299 | 125 | msg = REQUEST_PASSWORD_TOKEN_WRONG_EMAIL | 129 | msg = REQUEST_PASSWORD_TOKEN_WRONG_EMAIL |
1301 | 126 | self.show_error(self.app_name, msg) | 130 | self.show_error(msg) |
1302 | 127 | 131 | ||
1303 | === modified file 'ubuntu_sso/qt/loadingoverlay.py' | |||
1304 | --- ubuntu_sso/qt/loadingoverlay.py 2012-02-27 19:00:59 +0000 | |||
1305 | +++ ubuntu_sso/qt/loadingoverlay.py 2012-03-20 16:10:09 +0000 | |||
1306 | @@ -21,6 +21,8 @@ | |||
1307 | 21 | from ubuntu_sso.qt.ui import loadingoverlay_ui | 21 | from ubuntu_sso.qt.ui import loadingoverlay_ui |
1308 | 22 | from ubuntu_sso.utils.ui import LOADING_OVERLAY | 22 | from ubuntu_sso.utils.ui import LOADING_OVERLAY |
1309 | 23 | 23 | ||
1310 | 24 | LOADING_STYLE = u'<span style="font-size:x-large;">{0}</span>' | ||
1311 | 25 | |||
1312 | 24 | 26 | ||
1313 | 25 | class LoadingOverlay(QtGui.QFrame): | 27 | class LoadingOverlay(QtGui.QFrame): |
1314 | 26 | """The widget that shows a loading animation and disable the widget below. | 28 | """The widget that shows a loading animation and disable the widget below. |
1315 | @@ -43,7 +45,7 @@ | |||
1316 | 43 | self.counter = 0 | 45 | self.counter = 0 |
1317 | 44 | self.orientation = False | 46 | self.orientation = False |
1318 | 45 | 47 | ||
1320 | 46 | self.ui.label.setText(LOADING_OVERLAY) | 48 | self.ui.label.setText(LOADING_STYLE.format(LOADING_OVERLAY)) |
1321 | 47 | 49 | ||
1322 | 48 | # Invalid name "paintEvent", "eventFilter", "showEvent", "timerEvent" | 50 | # Invalid name "paintEvent", "eventFilter", "showEvent", "timerEvent" |
1323 | 49 | # pylint: disable=C0103 | 51 | # pylint: disable=C0103 |
1324 | 50 | 52 | ||
1325 | === modified file 'ubuntu_sso/qt/main.py' | |||
1326 | --- ubuntu_sso/qt/main.py 2012-02-24 19:54:48 +0000 | |||
1327 | +++ ubuntu_sso/qt/main.py 2012-03-20 16:10:09 +0000 | |||
1328 | @@ -25,6 +25,7 @@ | |||
1329 | 25 | from ubuntu_sso.qt.ui import resources_rc | 25 | from ubuntu_sso.qt.ui import resources_rc |
1330 | 26 | # pylint: enable=W0611 | 26 | # pylint: enable=W0611 |
1331 | 27 | from ubuntu_sso.qt.ubuntu_sso_wizard import UbuntuSSOClientGUI | 27 | from ubuntu_sso.qt.ubuntu_sso_wizard import UbuntuSSOClientGUI |
1332 | 28 | from ubuntu_sso.utils import PLATFORM_QSS | ||
1333 | 28 | 29 | ||
1334 | 29 | 30 | ||
1335 | 30 | def main(**kwargs): | 31 | def main(**kwargs): |
1336 | @@ -34,9 +35,17 @@ | |||
1337 | 34 | QtGui.QFontDatabase.addApplicationFont(':/Ubuntu-R.ttf') | 35 | QtGui.QFontDatabase.addApplicationFont(':/Ubuntu-R.ttf') |
1338 | 35 | QtGui.QFontDatabase.addApplicationFont(':/Ubuntu-B.ttf') | 36 | QtGui.QFontDatabase.addApplicationFont(':/Ubuntu-B.ttf') |
1339 | 36 | 37 | ||
1343 | 37 | # Apply Style Sheet -- The windows version may be different | 38 | data = [] |
1344 | 38 | qss = QtCore.QResource(":/stylesheet.qss") | 39 | for qss_name in (PLATFORM_QSS, ":/stylesheet.qss"): |
1345 | 39 | app.setStyleSheet(qss.data()) | 40 | qss = QtCore.QResource(qss_name) |
1346 | 41 | data.append(unicode(qss.data())) | ||
1347 | 42 | app.setStyleSheet('\n'.join(data)) | ||
1348 | 43 | |||
1349 | 44 | # Fix the string that contains unicode chars. | ||
1350 | 45 | for key in kwargs: | ||
1351 | 46 | value = kwargs[key] | ||
1352 | 47 | if isinstance(value, str): | ||
1353 | 48 | kwargs[key] = value.decode('utf-8') | ||
1354 | 40 | 49 | ||
1355 | 41 | # Unused variable 'ui', pylint: disable=W0612 | 50 | # Unused variable 'ui', pylint: disable=W0612 |
1356 | 42 | ui = UbuntuSSOClientGUI(close_callback=app.exit, **kwargs) | 51 | ui = UbuntuSSOClientGUI(close_callback=app.exit, **kwargs) |
1357 | 43 | 52 | ||
1358 | === modified file 'ubuntu_sso/qt/network_detection_page.py' | |||
1359 | --- ubuntu_sso/qt/network_detection_page.py 2012-02-29 14:00:12 +0000 | |||
1360 | +++ ubuntu_sso/qt/network_detection_page.py 2012-03-20 16:10:09 +0000 | |||
1361 | @@ -20,7 +20,7 @@ | |||
1362 | 20 | from PyQt4 import QtGui | 20 | from PyQt4 import QtGui |
1363 | 21 | 21 | ||
1364 | 22 | from ubuntu_sso import networkstate | 22 | from ubuntu_sso import networkstate |
1366 | 23 | 23 | from ubuntu_sso.logger import setup_logging | |
1367 | 24 | from ubuntu_sso.qt.sso_wizard_page import SSOWizardPage | 24 | from ubuntu_sso.qt.sso_wizard_page import SSOWizardPage |
1368 | 25 | from ubuntu_sso.qt.ui import network_detection_ui | 25 | from ubuntu_sso.qt.ui import network_detection_ui |
1369 | 26 | from ubuntu_sso.utils.ui import ( | 26 | from ubuntu_sso.utils.ui import ( |
1370 | @@ -31,6 +31,9 @@ | |||
1371 | 31 | ) | 31 | ) |
1372 | 32 | 32 | ||
1373 | 33 | 33 | ||
1374 | 34 | logger = setup_logging('ubuntu_sso.network_detection_page') | ||
1375 | 35 | |||
1376 | 36 | |||
1377 | 34 | class NetworkDetectionPage(SSOWizardPage): | 37 | class NetworkDetectionPage(SSOWizardPage): |
1378 | 35 | 38 | ||
1379 | 36 | """Widget to show if we don't detect a network connection.""" | 39 | """Widget to show if we don't detect a network connection.""" |
1380 | @@ -48,6 +51,7 @@ | |||
1381 | 48 | 51 | ||
1382 | 49 | def initializePage(self): | 52 | def initializePage(self): |
1383 | 50 | """Set UI details.""" | 53 | """Set UI details.""" |
1384 | 54 | logger.debug('initializePage - About to show NetworkDetectionPage') | ||
1385 | 51 | self.wizard()._next_id = None | 55 | self.wizard()._next_id = None |
1386 | 52 | 56 | ||
1387 | 53 | self.setButtonText(QtGui.QWizard.CustomButton1, TRY_AGAIN_BUTTON) | 57 | self.setButtonText(QtGui.QWizard.CustomButton1, TRY_AGAIN_BUTTON) |
1388 | 54 | 58 | ||
1389 | === modified file 'ubuntu_sso/qt/proxy_dialog.py' | |||
1390 | --- ubuntu_sso/qt/proxy_dialog.py 2012-03-05 20:30:57 +0000 | |||
1391 | +++ ubuntu_sso/qt/proxy_dialog.py 2012-03-20 16:10:09 +0000 | |||
1392 | @@ -21,6 +21,7 @@ | |||
1393 | 21 | from PyQt4.QtGui import QApplication, QDialog, QIcon | 21 | from PyQt4.QtGui import QApplication, QDialog, QIcon |
1394 | 22 | from twisted.internet import defer | 22 | from twisted.internet import defer |
1395 | 23 | 23 | ||
1396 | 24 | from ubuntu_sso import EXCEPTION_RAISED, USER_SUCCESS, USER_CANCELLATION | ||
1397 | 24 | from ubuntu_sso.logger import setup_gui_logging | 25 | from ubuntu_sso.logger import setup_gui_logging |
1398 | 25 | from ubuntu_sso.keyring import Keyring | 26 | from ubuntu_sso.keyring import Keyring |
1399 | 26 | from ubuntu_sso.qt.ui.proxy_credentials_dialog_ui import Ui_ProxyCredsDialog | 27 | from ubuntu_sso.qt.ui.proxy_credentials_dialog_ui import Ui_ProxyCredsDialog |
1400 | @@ -37,10 +38,6 @@ | |||
1401 | 37 | PROXY_CREDS_SAVE_BUTTON, | 38 | PROXY_CREDS_SAVE_BUTTON, |
1402 | 38 | ) | 39 | ) |
1403 | 39 | 40 | ||
1404 | 40 | CREDS_ACQUIRED = 0 | ||
1405 | 41 | USER_CANCELATION = -1 | ||
1406 | 42 | EXCEPTION_RAISED = -2 | ||
1407 | 43 | |||
1408 | 44 | logger = setup_gui_logging("ubuntu_sso.qt.proxy_dialog") | 41 | logger = setup_gui_logging("ubuntu_sso.qt.proxy_dialog") |
1409 | 45 | 42 | ||
1410 | 46 | 43 | ||
1411 | @@ -107,15 +104,15 @@ | |||
1412 | 107 | logger.debug('Save credentials as for domain %s.', self.domain) | 104 | logger.debug('Save credentials as for domain %s.', self.domain) |
1413 | 108 | yield self.keyring.set_credentials(self.domain, creds) | 105 | yield self.keyring.set_credentials(self.domain, creds) |
1414 | 109 | except Exception, e: | 106 | except Exception, e: |
1416 | 110 | logger.error('Could not retrieve credentials.') | 107 | logger.exception('Could not set credentials:') |
1417 | 111 | self.done(EXCEPTION_RAISED) | 108 | self.done(EXCEPTION_RAISED) |
1418 | 112 | # pylint: disable=W0703, W0612 | 109 | # pylint: disable=W0703, W0612 |
1420 | 113 | self.done(CREDS_ACQUIRED) | 110 | self.done(USER_SUCCESS) |
1421 | 114 | 111 | ||
1422 | 115 | def _on_cancel_clicked(self, *args): | 112 | def _on_cancel_clicked(self, *args): |
1423 | 116 | """End the dialog.""" | 113 | """End the dialog.""" |
1424 | 117 | logger.debug('User canceled credentials dialog.') | 114 | logger.debug('User canceled credentials dialog.') |
1426 | 118 | self.done(USER_CANCELATION) | 115 | self.done(USER_CANCELLATION) |
1427 | 119 | 116 | ||
1428 | 120 | def _set_buttons(self): | 117 | def _set_buttons(self): |
1429 | 121 | """Set the labels of the buttons.""" | 118 | """Set the labels of the buttons.""" |
1430 | 122 | 119 | ||
1431 | === modified file 'ubuntu_sso/qt/reset_password_page.py' | |||
1432 | --- ubuntu_sso/qt/reset_password_page.py 2012-03-05 20:30:57 +0000 | |||
1433 | +++ ubuntu_sso/qt/reset_password_page.py 2012-03-20 16:10:09 +0000 | |||
1434 | @@ -18,7 +18,7 @@ | |||
1435 | 18 | 18 | ||
1436 | 19 | from functools import partial | 19 | from functools import partial |
1437 | 20 | 20 | ||
1439 | 21 | from PyQt4.QtCore import SIGNAL, pyqtSignal | 21 | from PyQt4.QtCore import Qt, SIGNAL, pyqtSignal |
1440 | 22 | from PyQt4.QtGui import QApplication | 22 | from PyQt4.QtGui import QApplication |
1441 | 23 | 23 | ||
1442 | 24 | from ubuntu_sso import NO_OP | 24 | from ubuntu_sso import NO_OP |
1443 | @@ -73,7 +73,9 @@ | |||
1444 | 73 | 73 | ||
1445 | 74 | def initializePage(self): | 74 | def initializePage(self): |
1446 | 75 | """Extends QWizardPage initializePage method.""" | 75 | """Extends QWizardPage initializePage method.""" |
1447 | 76 | logger.debug('initializePage - About to show ResetPasswordPage') | ||
1448 | 76 | super(ResetPasswordPage, self).initializePage() | 77 | super(ResetPasswordPage, self).initializePage() |
1449 | 78 | self.ui.gridLayout.setAlignment(Qt.AlignLeft) | ||
1450 | 77 | common.password_default_assistance(self.ui.password_assistance) | 79 | common.password_default_assistance(self.ui.password_assistance) |
1451 | 78 | self.ui.password_assistance.setVisible(False) | 80 | self.ui.password_assistance.setVisible(False) |
1452 | 79 | self.setTitle(RESET_TITLE) | 81 | self.setTitle(RESET_TITLE) |
1453 | @@ -158,6 +160,8 @@ | |||
1454 | 158 | 160 | ||
1455 | 159 | def on_password_changed(self, app_name, email): | 161 | def on_password_changed(self, app_name, email): |
1456 | 160 | """Let user know that the password was changed.""" | 162 | """Let user know that the password was changed.""" |
1457 | 163 | logger.info('ResetPasswordPage.on_password_changed for %s, email: %s', | ||
1458 | 164 | app_name, email) | ||
1459 | 161 | self.hide_overlay() | 165 | self.hide_overlay() |
1460 | 162 | email = unicode(self.wizard().forgotten.ui.email_line_edit.text()) | 166 | email = unicode(self.wizard().forgotten.ui.email_line_edit.text()) |
1461 | 163 | self.passwordChanged.emit(email) | 167 | self.passwordChanged.emit(email) |
1462 | @@ -166,7 +170,7 @@ | |||
1463 | 166 | """Let the user know that there was an error.""" | 170 | """Let the user know that there was an error.""" |
1464 | 167 | logger.error('Got error changing password for %s, error: %s', | 171 | logger.error('Got error changing password for %s, error: %s', |
1465 | 168 | self.app_name, error) | 172 | self.app_name, error) |
1467 | 169 | self.show_error(self.app_name, build_general_error_message(error)) | 173 | self.show_error(build_general_error_message(error)) |
1468 | 170 | 174 | ||
1469 | 171 | def set_new_password(self): | 175 | def set_new_password(self): |
1470 | 172 | """Request a new password to be set.""" | 176 | """Request a new password to be set.""" |
1471 | 173 | 177 | ||
1472 | === modified file 'ubuntu_sso/qt/setup_account_page.py' | |||
1473 | --- ubuntu_sso/qt/setup_account_page.py 2012-03-06 14:13:10 +0000 | |||
1474 | +++ ubuntu_sso/qt/setup_account_page.py 2012-03-20 16:10:09 +0000 | |||
1475 | @@ -31,7 +31,7 @@ | |||
1476 | 31 | from PyQt4 import QtGui, QtCore | 31 | from PyQt4 import QtGui, QtCore |
1477 | 32 | 32 | ||
1478 | 33 | from ubuntu_sso import NO_OP | 33 | from ubuntu_sso import NO_OP |
1480 | 34 | from ubuntu_sso.logger import setup_gui_logging | 34 | from ubuntu_sso.logger import setup_gui_logging, log_call |
1481 | 35 | from ubuntu_sso.qt import ( | 35 | from ubuntu_sso.qt import ( |
1482 | 36 | LINK_STYLE, | 36 | LINK_STYLE, |
1483 | 37 | build_general_error_message, | 37 | build_general_error_message, |
1484 | @@ -112,11 +112,17 @@ | |||
1485 | 112 | } | 112 | } |
1486 | 113 | return result | 113 | return result |
1487 | 114 | 114 | ||
1488 | 115 | @property | ||
1489 | 116 | def password(self): | ||
1490 | 117 | """Return the content of the password edit.""" | ||
1491 | 118 | return unicode(self.ui.password_edit.text()) | ||
1492 | 119 | |||
1493 | 115 | # Invalid name "initializePage" | 120 | # Invalid name "initializePage" |
1494 | 116 | # pylint: disable=C0103 | 121 | # pylint: disable=C0103 |
1495 | 117 | 122 | ||
1496 | 118 | def initializePage(self): | 123 | def initializePage(self): |
1497 | 119 | """Setup UI details.""" | 124 | """Setup UI details.""" |
1498 | 125 | logger.debug('initializePage - About to show SetupAccountPage') | ||
1499 | 120 | # Set Setup Account button | 126 | # Set Setup Account button |
1500 | 121 | self.wizard().setOption(QtGui.QWizard.HaveCustomButton3, True) | 127 | self.wizard().setOption(QtGui.QWizard.HaveCustomButton3, True) |
1501 | 122 | try: | 128 | try: |
1502 | @@ -152,7 +158,6 @@ | |||
1503 | 152 | 158 | ||
1504 | 153 | def _set_translated_strings(self): | 159 | def _set_translated_strings(self): |
1505 | 154 | """Set the strings.""" | 160 | """Set the strings.""" |
1506 | 155 | logger.debug('SetUpAccountPage._set_translated_strings') | ||
1507 | 156 | # set the translated string | 161 | # set the translated string |
1508 | 157 | title_page = REGISTER_TITLE.format(app_name=self.app_name) | 162 | title_page = REGISTER_TITLE.format(app_name=self.app_name) |
1509 | 158 | self.setTitle(title_page) | 163 | self.setTitle(title_page) |
1510 | @@ -190,7 +195,7 @@ | |||
1511 | 190 | terms = AGREE_TO_PRIVACY_POLICY.format(app_name=self.app_name, | 195 | terms = AGREE_TO_PRIVACY_POLICY.format(app_name=self.app_name, |
1512 | 191 | privacy_policy=privacy_policy_link) | 196 | privacy_policy=privacy_policy_link) |
1513 | 192 | 197 | ||
1515 | 193 | self.terms_checkbox = enhanced_check_box.EnhancedCheckBox(terms) | 198 | self.terms_checkbox = enhanced_check_box.EnhancedCheckBox(terms, self) |
1516 | 194 | self.ui.hlayout_check.addWidget(self.terms_checkbox) | 199 | self.ui.hlayout_check.addWidget(self.terms_checkbox) |
1517 | 195 | self.terms_checkbox.setVisible(bool(self.tc_url or self.policy_url)) | 200 | self.terms_checkbox.setVisible(bool(self.tc_url or self.policy_url)) |
1518 | 196 | 201 | ||
1519 | @@ -220,7 +225,6 @@ | |||
1520 | 220 | 225 | ||
1521 | 221 | def _connect_ui(self): | 226 | def _connect_ui(self): |
1522 | 222 | """Set the connection of signals.""" | 227 | """Set the connection of signals.""" |
1523 | 223 | logger.debug('SetUpAccountPage._connect_ui') | ||
1524 | 224 | self._set_line_edits_validations() | 228 | self._set_line_edits_validations() |
1525 | 225 | 229 | ||
1526 | 226 | self.ui.captcha_view.setPixmap(QtGui.QPixmap()) | 230 | self.ui.captcha_view.setPixmap(QtGui.QPixmap()) |
1527 | @@ -229,6 +233,7 @@ | |||
1528 | 229 | self.ui.password_assistance, | 233 | self.ui.password_assistance, |
1529 | 230 | common.NORMAL)) | 234 | common.NORMAL)) |
1530 | 231 | 235 | ||
1531 | 236 | self.ui.refresh_label.linkActivated.connect(self.hide_error) | ||
1532 | 232 | self.ui.refresh_label.linkActivated.connect(lambda url: \ | 237 | self.ui.refresh_label.linkActivated.connect(lambda url: \ |
1533 | 233 | self._refresh_captcha()) | 238 | self._refresh_captcha()) |
1534 | 234 | # We need to check if we enable the button on many signals | 239 | # We need to check if we enable the button on many signals |
1535 | @@ -271,7 +276,6 @@ | |||
1536 | 271 | def _refresh_captcha(self): | 276 | def _refresh_captcha(self): |
1537 | 272 | """Refresh the captcha image shown in the ui.""" | 277 | """Refresh the captcha image shown in the ui.""" |
1538 | 273 | logger.debug('SetUpAccountPage._refresh_captcha') | 278 | logger.debug('SetUpAccountPage._refresh_captcha') |
1539 | 274 | self.hide_error() | ||
1540 | 275 | # lets clean behind us, do we have the old file arround? | 279 | # lets clean behind us, do we have the old file arround? |
1541 | 276 | if self.captcha_file and os.path.exists(self.captcha_file): | 280 | if self.captcha_file and os.path.exists(self.captcha_file): |
1542 | 277 | os.unlink(self.captcha_file) | 281 | os.unlink(self.captcha_file) |
1543 | @@ -297,11 +301,9 @@ | |||
1544 | 297 | self.registerField('email_address', self.ui.email_edit) | 301 | self.registerField('email_address', self.ui.email_edit) |
1545 | 298 | self.registerField('password', self.ui.password_edit) | 302 | self.registerField('password', self.ui.password_edit) |
1546 | 299 | 303 | ||
1547 | 304 | @log_call(logger.debug) | ||
1548 | 300 | def on_captcha_generated(self, app_name, result): | 305 | def on_captcha_generated(self, app_name, result): |
1549 | 301 | """A new image was generated.""" | 306 | """A new image was generated.""" |
1550 | 302 | logger.debug('SetUpAccountPage.on_captcha_generated for %r ' | ||
1551 | 303 | '(captcha id %r, filename %r).', | ||
1552 | 304 | app_name, result, self.captcha_file) | ||
1553 | 305 | self.captcha_id = result | 307 | self.captcha_id = result |
1554 | 306 | # HACK: First, let me apologize before hand, you can mention my mother | 308 | # HACK: First, let me apologize before hand, you can mention my mother |
1555 | 307 | # if needed I would do the same (mandel) | 309 | # if needed I would do the same (mandel) |
1556 | @@ -320,28 +322,28 @@ | |||
1557 | 320 | self.captcha_image = pixmap_image | 322 | self.captcha_image = pixmap_image |
1558 | 321 | self.on_captcha_refresh_complete() | 323 | self.on_captcha_refresh_complete() |
1559 | 322 | 324 | ||
1561 | 323 | def on_captcha_generation_error(self, error, *args, **kwargs): | 325 | @log_call(logger.error) |
1562 | 326 | def on_captcha_generation_error(self, app_name, error): | ||
1563 | 324 | """An error ocurred.""" | 327 | """An error ocurred.""" |
1566 | 325 | logger.debug('SetUpAccountPage.on_captcha_generation_error') | 328 | self.show_error(CAPTCHA_LOAD_ERROR) |
1565 | 326 | self.show_error(self.app_name, CAPTCHA_LOAD_ERROR) | ||
1567 | 327 | self.on_captcha_refresh_complete() | 329 | self.on_captcha_refresh_complete() |
1568 | 328 | 330 | ||
1569 | 331 | @log_call(logger.error) | ||
1570 | 329 | def on_user_registration_error(self, app_name, error): | 332 | def on_user_registration_error(self, app_name, error): |
1571 | 330 | """Let the user know we could not register.""" | 333 | """Let the user know we could not register.""" |
1572 | 331 | logger.debug('SetUpAccountPage.on_user_registration_error') | ||
1573 | 332 | # errors are returned as a dict with the data we want to show. | 334 | # errors are returned as a dict with the data we want to show. |
1574 | 333 | msg = error.pop(ERROR_EMAIL, '') | 335 | msg = error.pop(ERROR_EMAIL, '') |
1575 | 334 | if msg: | 336 | if msg: |
1576 | 335 | self.set_error_message(self.ui.email_assistance, msg) | 337 | self.set_error_message(self.ui.email_assistance, msg) |
1577 | 336 | error_msg = build_general_error_message(error) | 338 | error_msg = build_general_error_message(error) |
1578 | 337 | if error_msg: | 339 | if error_msg: |
1580 | 338 | self.show_error(self.app_name, error_msg) | 340 | self.show_error(error_msg) |
1581 | 339 | self._refresh_captcha() | 341 | self._refresh_captcha() |
1582 | 340 | 342 | ||
1583 | 343 | @log_call(logger.info) | ||
1584 | 341 | def on_user_registered(self, app_name, email): | 344 | def on_user_registered(self, app_name, email): |
1585 | 342 | """Execute when the user did register.""" | 345 | """Execute when the user did register.""" |
1586 | 343 | self.hide_overlay() | 346 | self.hide_overlay() |
1587 | 344 | logger.debug('SetUpAccountPage.on_user_registered') | ||
1588 | 345 | email = unicode(self.ui.email_edit.text()) | 347 | email = unicode(self.ui.email_edit.text()) |
1589 | 346 | self.userRegistered.emit(email) | 348 | self.userRegistered.emit(email) |
1590 | 347 | 349 | ||
1591 | @@ -376,7 +378,7 @@ | |||
1592 | 376 | messages.append(CAPTCHA_REQUIRED_ERROR) | 378 | messages.append(CAPTCHA_REQUIRED_ERROR) |
1593 | 377 | if len(messages) > 0: | 379 | if len(messages) > 0: |
1594 | 378 | condition = False | 380 | condition = False |
1596 | 379 | self.show_error(self.app_name, '\n'.join(messages)) | 381 | self.show_error('\n'.join(messages)) |
1597 | 380 | return condition | 382 | return condition |
1598 | 381 | 383 | ||
1599 | 382 | def set_next_validation(self): | 384 | def set_next_validation(self): |
1600 | @@ -401,17 +403,14 @@ | |||
1601 | 401 | 403 | ||
1602 | 402 | def is_correct_email(self, email_address): | 404 | def is_correct_email(self, email_address): |
1603 | 403 | """Return if the email is correct.""" | 405 | """Return if the email is correct.""" |
1604 | 404 | logger.debug('SetUpAccountPage.is_correct_email') | ||
1605 | 405 | return '@' in email_address | 406 | return '@' in email_address |
1606 | 406 | 407 | ||
1607 | 407 | def is_correct_email_confirmation(self, email_address): | 408 | def is_correct_email_confirmation(self, email_address): |
1608 | 408 | """Return that the email is the same.""" | 409 | """Return that the email is the same.""" |
1609 | 409 | logger.debug('SetUpAccountPage.is_correct_email_confirmation') | ||
1610 | 410 | return unicode(self.ui.email_edit.text()) == email_address | 410 | return unicode(self.ui.email_edit.text()) == email_address |
1611 | 411 | 411 | ||
1612 | 412 | def is_correct_password_confirmation(self, password): | 412 | def is_correct_password_confirmation(self, password): |
1613 | 413 | """Return that the passwords are correct.""" | 413 | """Return that the passwords are correct.""" |
1614 | 414 | logger.debug('SetUpAccountPage.is_correct_password_confirmation') | ||
1615 | 415 | return unicode(self.ui.password_edit.text()) == password | 414 | return unicode(self.ui.password_edit.text()) == password |
1616 | 416 | 415 | ||
1617 | 417 | def focus_changed(self, old, now): | 416 | def focus_changed(self, old, now): |
1618 | @@ -500,12 +499,14 @@ | |||
1619 | 500 | 499 | ||
1620 | 501 | def on_captcha_refreshing(self): | 500 | def on_captcha_refreshing(self): |
1621 | 502 | """Show overlay when captcha is refreshing.""" | 501 | """Show overlay when captcha is refreshing.""" |
1622 | 502 | logger.info('SetUpAccountPage.on_captcha_refreshing') | ||
1623 | 503 | if self.isVisible(): | 503 | if self.isVisible(): |
1624 | 504 | self.show_overlay() | 504 | self.show_overlay() |
1625 | 505 | self.captcha_received = False | 505 | self.captcha_received = False |
1626 | 506 | 506 | ||
1627 | 507 | def on_captcha_refresh_complete(self): | 507 | def on_captcha_refresh_complete(self): |
1628 | 508 | """Hide overlay when captcha finished refreshing.""" | 508 | """Hide overlay when captcha finished refreshing.""" |
1629 | 509 | logger.info('SetUpAccountPage.on_captcha_refresh_complete') | ||
1630 | 509 | self.hide_overlay() | 510 | self.hide_overlay() |
1631 | 510 | self.captcha_received = True | 511 | self.captcha_received = True |
1632 | 511 | 512 | ||
1633 | 512 | 513 | ||
1634 | === modified file 'ubuntu_sso/qt/sso_wizard_page.py' | |||
1635 | --- ubuntu_sso/qt/sso_wizard_page.py 2012-03-05 20:30:57 +0000 | |||
1636 | +++ ubuntu_sso/qt/sso_wizard_page.py 2012-03-20 16:10:09 +0000 | |||
1637 | @@ -23,31 +23,38 @@ | |||
1638 | 23 | QApplication, | 23 | QApplication, |
1639 | 24 | QCursor, | 24 | QCursor, |
1640 | 25 | QFrame, | 25 | QFrame, |
1641 | 26 | QFontMetrics, | ||
1642 | 27 | QHBoxLayout, | 26 | QHBoxLayout, |
1643 | 27 | QLabel, | ||
1644 | 28 | QStyle, | ||
1645 | 28 | QVBoxLayout, | 29 | QVBoxLayout, |
1646 | 29 | QStyle, | ||
1647 | 30 | QWizardPage, | 30 | QWizardPage, |
1648 | 31 | QLabel, | ||
1649 | 32 | ) | 31 | ) |
1650 | 33 | from twisted.internet import defer | 32 | from twisted.internet import defer |
1651 | 34 | 33 | ||
1652 | 35 | from ubuntu_sso import main | 34 | from ubuntu_sso import main |
1655 | 36 | from ubuntu_sso.logger import setup_gui_logging | 35 | from ubuntu_sso.logger import setup_gui_logging, log_call |
1656 | 37 | from ubuntu_sso.qt import PREFERED_UI_SIZE, TITLE_STYLE | 36 | from ubuntu_sso.qt import ( |
1657 | 37 | ERROR_STYLE, | ||
1658 | 38 | maybe_elide_text, | ||
1659 | 39 | PREFERED_UI_SIZE, | ||
1660 | 40 | TITLE_STYLE, | ||
1661 | 41 | ) | ||
1662 | 38 | from ubuntu_sso.utils.ui import GENERIC_BACKEND_ERROR | 42 | from ubuntu_sso.utils.ui import GENERIC_BACKEND_ERROR |
1663 | 39 | 43 | ||
1664 | 40 | 44 | ||
1665 | 41 | logger = setup_gui_logging('ubuntu_sso.sso_wizard_page') | 45 | logger = setup_gui_logging('ubuntu_sso.sso_wizard_page') |
1666 | 42 | 46 | ||
1667 | 43 | 47 | ||
1670 | 44 | class Header(QFrame): | 48 | class WizardHeader(QFrame): |
1671 | 45 | """Header Class for Title and Subtitle in all wizard pages.""" | 49 | """WizardHeader Class for Title and Subtitle in all wizard pages.""" |
1672 | 46 | 50 | ||
1674 | 47 | def __init__(self): | 51 | def __init__(self, max_width, parent=None): |
1675 | 48 | """Create a new instance.""" | 52 | """Create a new instance.""" |
1678 | 49 | super(Header, self).__init__() | 53 | super(WizardHeader, self).__init__(parent=parent) |
1679 | 50 | self.setObjectName('header') | 54 | self.max_width = max_width |
1680 | 55 | self.max_title_width = self.max_width * 0.95 | ||
1681 | 56 | self.max_subtitle_width = self.max_width * 1.8 | ||
1682 | 57 | |||
1683 | 51 | vbox = QVBoxLayout(self) | 58 | vbox = QVBoxLayout(self) |
1684 | 52 | vbox.setContentsMargins(0, 0, 0, 0) | 59 | vbox.setContentsMargins(0, 0, 0, 0) |
1685 | 53 | self.title_label = QLabel() | 60 | self.title_label = QLabel() |
1686 | @@ -64,11 +71,8 @@ | |||
1687 | 64 | def set_title(self, title): | 71 | def set_title(self, title): |
1688 | 65 | """Set the Title of the page or hide it otherwise""" | 72 | """Set the Title of the page or hide it otherwise""" |
1689 | 66 | if title: | 73 | if title: |
1695 | 67 | fm = QFontMetrics(self.subtitle_label.font()) | 74 | maybe_elide_text(self.title_label, title, self.max_title_width, |
1696 | 68 | width = PREFERED_UI_SIZE['width'] * 0.95 | 75 | markup=TITLE_STYLE) |
1692 | 69 | elided_text = fm.elidedText(title, Qt.ElideRight, width) | ||
1693 | 70 | self.title_label.setToolTip(title) | ||
1694 | 71 | self.title_label.setText(elided_text) | ||
1697 | 72 | self.title_label.setVisible(True) | 76 | self.title_label.setVisible(True) |
1698 | 73 | else: | 77 | else: |
1699 | 74 | self.title_label.setVisible(False) | 78 | self.title_label.setVisible(False) |
1700 | @@ -76,28 +80,23 @@ | |||
1701 | 76 | def set_subtitle(self, subtitle): | 80 | def set_subtitle(self, subtitle): |
1702 | 77 | """Set the Subtitle of the page or hide it otherwise""" | 81 | """Set the Subtitle of the page or hide it otherwise""" |
1703 | 78 | if subtitle: | 82 | if subtitle: |
1709 | 79 | fm = QFontMetrics(self.subtitle_label.font()) | 83 | maybe_elide_text(self.subtitle_label, subtitle, |
1710 | 80 | width = PREFERED_UI_SIZE['width'] * 1.8 | 84 | self.max_subtitle_width) |
1706 | 81 | elided_text = fm.elidedText(subtitle, Qt.ElideRight, width) | ||
1707 | 82 | self.subtitle_label.setText(elided_text) | ||
1708 | 83 | self.subtitle_label.setToolTip(subtitle) | ||
1711 | 84 | self.subtitle_label.setVisible(True) | 85 | self.subtitle_label.setVisible(True) |
1712 | 85 | else: | 86 | else: |
1713 | 86 | self.subtitle_label.setVisible(False) | 87 | self.subtitle_label.setVisible(False) |
1714 | 87 | 88 | ||
1715 | 88 | 89 | ||
1718 | 89 | class SSOWizardPage(QWizardPage): | 90 | class BaseWizardPage(QWizardPage): |
1719 | 90 | """Root class for all wizard pages.""" | 91 | """Base class for all wizard pages.""" |
1720 | 91 | 92 | ||
1721 | 92 | ui_class = None | 93 | ui_class = None |
1723 | 93 | _signals = {} # override in children | 94 | max_width = 0 |
1724 | 94 | processingStarted = pyqtSignal() | 95 | processingStarted = pyqtSignal() |
1725 | 95 | processingFinished = pyqtSignal() | 96 | processingFinished = pyqtSignal() |
1726 | 96 | 97 | ||
1731 | 97 | def __init__(self, app_name, **kwargs): | 98 | def __init__(self, parent=None): |
1732 | 98 | """Create a new instance.""" | 99 | super(BaseWizardPage, self).__init__(parent=parent) |
1729 | 99 | parent = kwargs.pop('parent', None) | ||
1730 | 100 | super(SSOWizardPage, self).__init__(parent=parent) | ||
1733 | 101 | 100 | ||
1734 | 102 | self.ui = None | 101 | self.ui = None |
1735 | 103 | if self.ui_class is not None: | 102 | if self.ui_class is not None: |
1736 | @@ -105,64 +104,39 @@ | |||
1737 | 105 | self.ui = self.ui_class() | 104 | self.ui = self.ui_class() |
1738 | 106 | self.ui.setupUi(self) | 105 | self.ui.setupUi(self) |
1739 | 107 | 106 | ||
1746 | 108 | # store common useful data provided by the app | 107 | if self.layout() is None: |
1747 | 109 | self.app_name = app_name | 108 | self.setLayout(QVBoxLayout(self)) |
1742 | 110 | self.ping_url = kwargs.get('ping_url', '') | ||
1743 | 111 | self.tc_url = kwargs.get('tc_url', '') | ||
1744 | 112 | self.policy_url = kwargs.get('policy_url', '') | ||
1745 | 113 | self.help_text = kwargs.get('help_text', '') | ||
1748 | 114 | 109 | ||
1749 | 115 | # Set the error area | 110 | # Set the error area |
1751 | 116 | self.form_errors_label = QLabel(' ') | 111 | self.form_errors_label = QLabel() |
1752 | 117 | self.form_errors_label.setObjectName('form_errors') | 112 | self.form_errors_label.setObjectName('form_errors') |
1753 | 118 | self.form_errors_label.setAlignment(Qt.AlignBottom) | 113 | self.form_errors_label.setAlignment(Qt.AlignBottom) |
1754 | 119 | self.layout().insertWidget(0, self.form_errors_label) | 114 | self.layout().insertWidget(0, self.form_errors_label) |
1755 | 115 | |||
1756 | 120 | # Set the header | 116 | # Set the header |
1758 | 121 | self.header = Header() | 117 | self.header = WizardHeader(max_width=self.max_width) |
1759 | 122 | self.header.set_title(title='') | 118 | self.header.set_title(title='') |
1760 | 123 | self.header.set_subtitle(subtitle='') | 119 | self.header.set_subtitle(subtitle='') |
1761 | 124 | self.layout().insertWidget(0, self.header) | 120 | self.layout().insertWidget(0, self.header) |
1762 | 125 | self._signals_receivers = {} | ||
1763 | 126 | self.backend = None | ||
1764 | 127 | 121 | ||
1765 | 128 | self.layout().setAlignment(Qt.AlignLeft) | 122 | self.layout().setAlignment(Qt.AlignLeft) |
1766 | 129 | 123 | ||
1803 | 130 | self.setup_page() | 124 | self._is_processing = False |
1804 | 131 | 125 | ||
1805 | 132 | def show_error(self, app_name, message): | 126 | def _get_is_processing(self): |
1806 | 133 | """Show an error message inside the page.""" | 127 | """Is this widget processing any request?""" |
1807 | 134 | self.hide_overlay() | 128 | return self._is_processing |
1808 | 135 | fm = QFontMetrics(self.form_errors_label.font()) | 129 | |
1809 | 136 | width = PREFERED_UI_SIZE['width'] * 0.95 | 130 | def _set_is_processing(self, new_value): |
1810 | 137 | elided_text = fm.elidedText(message, Qt.ElideRight, width) | 131 | """Set this widget to be processing a request.""" |
1811 | 138 | self.form_errors_label.setText(elided_text) | 132 | self._is_processing = new_value |
1812 | 139 | self.form_errors_label.setToolTip(message) | 133 | self.setEnabled(not new_value) |
1813 | 140 | 134 | if not self._is_processing: | |
1814 | 141 | def hide_error(self): | 135 | self.processingFinished.emit() |
1815 | 142 | """Hide the label errors in the current page.""" | 136 | else: |
1816 | 143 | # We actually want the label with one chat, because if it is an | 137 | self.processingStarted.emit() |
1817 | 144 | # empty string, the height of the label is 0 | 138 | |
1818 | 145 | self.form_errors_label.setText(' ') | 139 | is_processing = property(fget=_get_is_processing, fset=_set_is_processing) |
1783 | 146 | |||
1784 | 147 | def hide_overlay(self): | ||
1785 | 148 | """Emit the signal to notify the upper container that ends loading.""" | ||
1786 | 149 | self.setEnabled(True) | ||
1787 | 150 | self.processingFinished.emit() | ||
1788 | 151 | |||
1789 | 152 | def show_overlay(self): | ||
1790 | 153 | """Emit the signal to notify the upper container that is loading.""" | ||
1791 | 154 | self.setEnabled(False) | ||
1792 | 155 | self.processingStarted.emit() | ||
1793 | 156 | |||
1794 | 157 | @defer.inlineCallbacks | ||
1795 | 158 | def setup_page(self): | ||
1796 | 159 | """Setup the widget components.""" | ||
1797 | 160 | client = yield main.get_sso_client() | ||
1798 | 161 | self.backend = client.sso_login | ||
1799 | 162 | |||
1800 | 163 | self._setup_signals() | ||
1801 | 164 | self._set_translated_strings() | ||
1802 | 165 | self._connect_ui() | ||
1819 | 166 | 140 | ||
1820 | 167 | # pylint: disable=C0103 | 141 | # pylint: disable=C0103 |
1821 | 168 | 142 | ||
1822 | @@ -172,7 +146,7 @@ | |||
1823 | 172 | 146 | ||
1824 | 173 | def setTitle(self, title=''): | 147 | def setTitle(self, title=''): |
1825 | 174 | """Set the Wizard Page Title.""" | 148 | """Set the Wizard Page Title.""" |
1827 | 175 | self.header.set_title(TITLE_STYLE % title) | 149 | self.header.set_title(title) |
1828 | 176 | 150 | ||
1829 | 177 | def setSubTitle(self, subtitle=''): | 151 | def setSubTitle(self, subtitle=''): |
1830 | 178 | """Set the Wizard Page Subtitle.""" | 152 | """Set the Wizard Page Subtitle.""" |
1831 | @@ -188,6 +162,74 @@ | |||
1832 | 188 | 162 | ||
1833 | 189 | # pylint: enable=C0103 | 163 | # pylint: enable=C0103 |
1834 | 190 | 164 | ||
1835 | 165 | @log_call(logger.error) | ||
1836 | 166 | def show_error(self, message): | ||
1837 | 167 | """Show an error message inside the page.""" | ||
1838 | 168 | self.is_processing = False | ||
1839 | 169 | maybe_elide_text(self.form_errors_label, message, | ||
1840 | 170 | self.max_width * 0.95, markup=ERROR_STYLE) | ||
1841 | 171 | |||
1842 | 172 | def hide_error(self): | ||
1843 | 173 | """Hide the label errors in the current page.""" | ||
1844 | 174 | # We actually want the label with one empty char, because if it is an | ||
1845 | 175 | # empty string, the height of the label is 0 | ||
1846 | 176 | self.form_errors_label.setText(' ') | ||
1847 | 177 | |||
1848 | 178 | |||
1849 | 179 | class SSOWizardPage(BaseWizardPage): | ||
1850 | 180 | """Root class for all SSO specific wizard pages.""" | ||
1851 | 181 | |||
1852 | 182 | _signals = {} # override in children | ||
1853 | 183 | max_width = PREFERED_UI_SIZE['width'] | ||
1854 | 184 | |||
1855 | 185 | def __init__(self, app_name, **kwargs): | ||
1856 | 186 | """Create a new instance.""" | ||
1857 | 187 | parent = kwargs.pop('parent', None) | ||
1858 | 188 | super(SSOWizardPage, self).__init__(parent=parent) | ||
1859 | 189 | |||
1860 | 190 | # store common useful data provided by the app | ||
1861 | 191 | self.app_name = app_name | ||
1862 | 192 | self.ping_url = kwargs.get('ping_url', '') | ||
1863 | 193 | self.tc_url = kwargs.get('tc_url', '') | ||
1864 | 194 | self.policy_url = kwargs.get('policy_url', '') | ||
1865 | 195 | self.help_text = kwargs.get('help_text', '') | ||
1866 | 196 | |||
1867 | 197 | self._signals_receivers = {} | ||
1868 | 198 | self.backend = None | ||
1869 | 199 | |||
1870 | 200 | self.setup_page() | ||
1871 | 201 | |||
1872 | 202 | def hide_overlay(self): | ||
1873 | 203 | """Emit the signal to notify the upper container that ends loading.""" | ||
1874 | 204 | self.is_processing = False | ||
1875 | 205 | |||
1876 | 206 | def show_overlay(self): | ||
1877 | 207 | """Emit the signal to notify the upper container that is loading.""" | ||
1878 | 208 | self.is_processing = True | ||
1879 | 209 | |||
1880 | 210 | @defer.inlineCallbacks | ||
1881 | 211 | def setup_page(self): | ||
1882 | 212 | """Setup the widget components.""" | ||
1883 | 213 | logger.info('Starting setup_page for: %r', self) | ||
1884 | 214 | # pylint: disable=W0702,W0703 | ||
1885 | 215 | try: | ||
1886 | 216 | # Get Backend | ||
1887 | 217 | client = yield main.get_sso_client() | ||
1888 | 218 | self.backend = client.sso_login | ||
1889 | 219 | self._set_translated_strings() | ||
1890 | 220 | self._connect_ui() | ||
1891 | 221 | # Call _setup_signals at the end, so we ensure that the UI | ||
1892 | 222 | # is at least styled as expected if the operations with the | ||
1893 | 223 | # backend fails. | ||
1894 | 224 | self._setup_signals() | ||
1895 | 225 | except: | ||
1896 | 226 | message = 'There was a problem trying to setup the page %r' % self | ||
1897 | 227 | self.show_error(message) | ||
1898 | 228 | logger.exception(message) | ||
1899 | 229 | self.setEnabled(False) | ||
1900 | 230 | # pylint: enable=W0702,W0703 | ||
1901 | 231 | logger.info('%r - setup_page ends, backend is %r.', self, self.backend) | ||
1902 | 232 | |||
1903 | 191 | def _filter_by_app_name(self, f): | 233 | def _filter_by_app_name(self, f): |
1904 | 192 | """Excecute the decorated function only for 'self.app_name'.""" | 234 | """Excecute the decorated function only for 'self.app_name'.""" |
1905 | 193 | 235 | ||
1906 | @@ -218,11 +260,9 @@ | |||
1907 | 218 | 260 | ||
1908 | 219 | def _set_translated_strings(self): | 261 | def _set_translated_strings(self): |
1909 | 220 | """Implement in each child.""" | 262 | """Implement in each child.""" |
1910 | 221 | raise NotImplementedError() | ||
1911 | 222 | 263 | ||
1912 | 223 | def _connect_ui(self): | 264 | def _connect_ui(self): |
1913 | 224 | """Implement in each child.""" | 265 | """Implement in each child.""" |
1914 | 225 | raise NotImplementedError() | ||
1915 | 226 | 266 | ||
1916 | 227 | def _handle_error(self, remote_call, handler, error): | 267 | def _handle_error(self, remote_call, handler, error): |
1917 | 228 | """Handle any error when calling the remote backend.""" | 268 | """Handle any error when calling the remote backend.""" |
1918 | 229 | 269 | ||
1919 | === modified file 'ubuntu_sso/qt/tests/__init__.py' | |||
1920 | --- ubuntu_sso/qt/tests/__init__.py 2012-03-05 20:30:57 +0000 | |||
1921 | +++ ubuntu_sso/qt/tests/__init__.py 2012-03-20 16:10:09 +0000 | |||
1922 | @@ -21,7 +21,7 @@ | |||
1923 | 21 | from twisted.trial.unittest import TestCase | 21 | from twisted.trial.unittest import TestCase |
1924 | 22 | 22 | ||
1925 | 23 | from ubuntu_sso import main, NO_OP | 23 | from ubuntu_sso import main, NO_OP |
1927 | 24 | from ubuntu_sso.qt import TITLE_STYLE | 24 | from ubuntu_sso.qt import ERROR_STYLE, maybe_elide_text, TITLE_STYLE |
1928 | 25 | from ubuntu_sso.tests import ( | 25 | from ubuntu_sso.tests import ( |
1929 | 26 | APP_NAME, | 26 | APP_NAME, |
1930 | 27 | HELP_TEXT, | 27 | HELP_TEXT, |
1931 | @@ -38,6 +38,15 @@ | |||
1932 | 38 | # pylint: disable=W0212 | 38 | # pylint: disable=W0212 |
1933 | 39 | 39 | ||
1934 | 40 | 40 | ||
1935 | 41 | def build_string_for_pixels(label, width): | ||
1936 | 42 | """Return a random string that will be as big as with in pixels.""" | ||
1937 | 43 | char = 'a' | ||
1938 | 44 | fm = QtGui.QFontMetrics(label.font()) | ||
1939 | 45 | pixel_width = fm.width(char) | ||
1940 | 46 | chars = int(width / pixel_width) | ||
1941 | 47 | return char * chars | ||
1942 | 48 | |||
1943 | 49 | |||
1944 | 41 | class FakedObject(object): | 50 | class FakedObject(object): |
1945 | 42 | """Fake an object, record every call.""" | 51 | """Fake an object, record every call.""" |
1946 | 43 | 52 | ||
1947 | @@ -439,6 +448,9 @@ | |||
1948 | 439 | self.wizard = self.ui_wizard_class() | 448 | self.wizard = self.ui_wizard_class() |
1949 | 440 | self.patch(self.ui, 'wizard', lambda: self.wizard) | 449 | self.patch(self.ui, 'wizard', lambda: self.wizard) |
1950 | 441 | 450 | ||
1951 | 451 | self.ui.show() | ||
1952 | 452 | self.addCleanup(self.ui.hide) | ||
1953 | 453 | |||
1954 | 442 | def _set_called(self, *args, **kwargs): | 454 | def _set_called(self, *args, **kwargs): |
1955 | 443 | """Store 'args' and 'kwargs' for test assertions.""" | 455 | """Store 'args' and 'kwargs' for test assertions.""" |
1956 | 444 | self._called = (args, kwargs) | 456 | self._called = (args, kwargs) |
1957 | @@ -467,6 +479,38 @@ | |||
1958 | 467 | 479 | ||
1959 | 468 | self.assertEqual(self.signal_results, [signal_args]) | 480 | self.assertEqual(self.signal_results, [signal_args]) |
1960 | 469 | 481 | ||
1961 | 482 | def assert_title_correct(self, title_label, expected, max_width): | ||
1962 | 483 | """Check that the label's text is equal to 'expected'.""" | ||
1963 | 484 | label = QtGui.QLabel() | ||
1964 | 485 | maybe_elide_text(label, expected, max_width) | ||
1965 | 486 | |||
1966 | 487 | self.assertEqual(TITLE_STYLE % unicode(label.text()), | ||
1967 | 488 | unicode(title_label.text())) | ||
1968 | 489 | self.assertEqual(unicode(label.toolTip()), | ||
1969 | 490 | unicode(title_label.toolTip())) | ||
1970 | 491 | self.assertTrue(title_label.isVisible()) | ||
1971 | 492 | |||
1972 | 493 | def assert_subtitle_correct(self, subtitle_label, expected, max_width): | ||
1973 | 494 | """Check that the subtitle is equal to 'expected'.""" | ||
1974 | 495 | label = QtGui.QLabel() | ||
1975 | 496 | maybe_elide_text(label, expected, max_width) | ||
1976 | 497 | |||
1977 | 498 | self.assertEqual(unicode(label.text()), unicode(subtitle_label.text())) | ||
1978 | 499 | self.assertEqual(unicode(label.toolTip()), | ||
1979 | 500 | unicode(subtitle_label.toolTip())) | ||
1980 | 501 | self.assertTrue(subtitle_label.isVisible()) | ||
1981 | 502 | |||
1982 | 503 | def assert_error_correct(self, error_label, expected, max_width): | ||
1983 | 504 | """Check that the error 'error_label' displays 'expected' as text.""" | ||
1984 | 505 | label = QtGui.QLabel() | ||
1985 | 506 | maybe_elide_text(label, expected, max_width) | ||
1986 | 507 | |||
1987 | 508 | self.assertEqual(ERROR_STYLE % unicode(label.text()), | ||
1988 | 509 | unicode(error_label.text())) | ||
1989 | 510 | self.assertEqual(unicode(label.toolTip()), | ||
1990 | 511 | unicode(error_label.toolTip())) | ||
1991 | 512 | self.assertTrue(error_label.isVisible()) | ||
1992 | 513 | |||
1993 | 470 | def get_pixmap_data(self, pixmap): | 514 | def get_pixmap_data(self, pixmap): |
1994 | 471 | """Get the raw data of a QPixmap.""" | 515 | """Get the raw data of a QPixmap.""" |
1995 | 472 | byte_array = QtCore.QByteArray() | 516 | byte_array = QtCore.QByteArray() |
1996 | @@ -507,7 +551,7 @@ | |||
1997 | 507 | self.assertIn(signal, self.ui._signals) | 551 | self.assertIn(signal, self.ui._signals) |
1998 | 508 | self.assertTrue(callable(self.ui._signals[signal])) | 552 | self.assertTrue(callable(self.ui._signals[signal])) |
1999 | 509 | 553 | ||
2001 | 510 | expected = ['_setup_signals', '_set_translated_strings', '_connect_ui'] | 554 | expected = ['_set_translated_strings', '_connect_ui', '_setup_signals'] |
2002 | 511 | self.assertEqual(expected, called) | 555 | self.assertEqual(expected, called) |
2003 | 512 | 556 | ||
2004 | 513 | 557 | ||
2005 | @@ -550,14 +594,18 @@ | |||
2006 | 550 | self.assertEqual(self._overlay_hide_counter, 1) | 594 | self.assertEqual(self._overlay_hide_counter, 1) |
2007 | 551 | self.assertTrue(self.ui.isEnabled()) | 595 | self.assertTrue(self.ui.isEnabled()) |
2008 | 552 | 596 | ||
2009 | 597 | # pylint: disable=W0221 | ||
2010 | 598 | |||
2011 | 553 | def assert_title_correct(self, expected): | 599 | def assert_title_correct(self, expected): |
2012 | 554 | """Check that the title is equal to 'expected'.""" | 600 | """Check that the title is equal to 'expected'.""" |
2014 | 555 | self.assertEqual(TITLE_STYLE % expected, unicode(self.ui.title())) | 601 | check = super(PageBaseTestCase, self).assert_title_correct |
2015 | 602 | check(self.ui.header.title_label, expected, | ||
2016 | 603 | self.ui.header.max_title_width) | ||
2017 | 556 | 604 | ||
2018 | 557 | def assert_subtitle_correct(self, expected): | 605 | def assert_subtitle_correct(self, expected): |
2019 | 558 | """Check that the subtitle is equal to 'expected'.""" | 606 | """Check that the subtitle is equal to 'expected'.""" |
2022 | 559 | elided_text = unicode(self.ui.subTitle()) | 607 | check = super(PageBaseTestCase, self).assert_subtitle_correct |
2023 | 560 | elided_text = elided_text[:len(elided_text) - 1] | 608 | check(self.ui.header.subtitle_label, expected, |
2024 | 609 | self.ui.header.max_subtitle_width) | ||
2025 | 561 | 610 | ||
2028 | 562 | self.assertTrue(expected.startswith(elided_text)) | 611 | # pylint: enable=W0221 |
2027 | 563 | self.assertEqual(self.ui.header.subtitle_label.toolTip(), expected) | ||
2029 | 564 | 612 | ||
2030 | === modified file 'ubuntu_sso/qt/tests/login_u_p.py' | |||
2031 | --- ubuntu_sso/qt/tests/login_u_p.py 2012-01-26 15:34:16 +0000 | |||
2032 | +++ ubuntu_sso/qt/tests/login_u_p.py 2012-03-20 16:10:09 +0000 | |||
2033 | @@ -39,7 +39,6 @@ | |||
2034 | 39 | 39 | ||
2035 | 40 | def found(*args): | 40 | def found(*args): |
2036 | 41 | """The result was received.""" | 41 | """The result was received.""" |
2037 | 42 | print "result received", args | ||
2038 | 43 | d.callback(args) | 42 | d.callback(args) |
2039 | 44 | 43 | ||
2040 | 45 | client.cred_manager.connect_to_signal('CredentialsFound', found) | 44 | client.cred_manager.connect_to_signal('CredentialsFound', found) |
2041 | @@ -61,7 +60,6 @@ | |||
2042 | 61 | yield cleared | 60 | yield cleared |
2043 | 62 | 61 | ||
2044 | 63 | yield client.cred_manager.login_email_password('SUPER', args) | 62 | yield client.cred_manager.login_email_password('SUPER', args) |
2045 | 64 | print "called ok" | ||
2046 | 65 | yield d | 63 | yield d |
2047 | 66 | 64 | ||
2048 | 67 | yield client.disconnect() | 65 | yield client.disconnect() |
2049 | 68 | 66 | ||
2050 | === modified file 'ubuntu_sso/qt/tests/show_gui.py' | |||
2051 | --- ubuntu_sso/qt/tests/show_gui.py 2012-02-10 17:18:22 +0000 | |||
2052 | +++ ubuntu_sso/qt/tests/show_gui.py 2012-03-20 16:10:09 +0000 | |||
2053 | @@ -46,7 +46,6 @@ | |||
2054 | 46 | 46 | ||
2055 | 47 | def found(*args): | 47 | def found(*args): |
2056 | 48 | """The result was received.""" | 48 | """The result was received.""" |
2057 | 49 | print "result received", args | ||
2058 | 50 | d.callback(args) | 49 | d.callback(args) |
2059 | 51 | 50 | ||
2060 | 52 | client.cred_manager.connect_to_signal('CredentialsFound', found) | 51 | client.cred_manager.connect_to_signal('CredentialsFound', found) |
2061 | @@ -62,7 +61,6 @@ | |||
2062 | 62 | TC_URL_KEY: u'http://www.google.com/', | 61 | TC_URL_KEY: u'http://www.google.com/', |
2063 | 63 | UI_EXECUTABLE_KEY: 'ubuntu-sso-login-gtk', | 62 | UI_EXECUTABLE_KEY: 'ubuntu-sso-login-gtk', |
2064 | 64 | }) | 63 | }) |
2065 | 65 | print "called ok" | ||
2066 | 66 | yield d | 64 | yield d |
2067 | 67 | 65 | ||
2068 | 68 | yield client.disconnect() | 66 | yield client.disconnect() |
2069 | 69 | 67 | ||
2070 | === modified file 'ubuntu_sso/qt/tests/test_common.py' | |||
2071 | --- ubuntu_sso/qt/tests/test_common.py 2011-10-28 10:41:18 +0000 | |||
2072 | +++ ubuntu_sso/qt/tests/test_common.py 2012-03-20 16:10:09 +0000 | |||
2073 | @@ -20,7 +20,13 @@ | |||
2074 | 20 | from twisted.internet import defer | 20 | from twisted.internet import defer |
2075 | 21 | from twisted.trial.unittest import TestCase | 21 | from twisted.trial.unittest import TestCase |
2076 | 22 | 22 | ||
2078 | 23 | from ubuntu_sso.qt.common import (check_as_invalid, | 23 | from ubuntu_sso.qt import ( |
2079 | 24 | build_general_error_message, | ||
2080 | 25 | maybe_elide_text, | ||
2081 | 26 | GENERIC_BACKEND_ERROR, | ||
2082 | 27 | ) | ||
2083 | 28 | from ubuntu_sso.qt.common import ( | ||
2084 | 29 | check_as_invalid, | ||
2085 | 24 | check_as_valid, | 30 | check_as_valid, |
2086 | 25 | password_assistance, | 31 | password_assistance, |
2087 | 26 | password_check_match, | 32 | password_check_match, |
2088 | @@ -30,7 +36,9 @@ | |||
2089 | 30 | PASSWORD_DIGIT, | 36 | PASSWORD_DIGIT, |
2090 | 31 | PASSWORD_LENGTH, | 37 | PASSWORD_LENGTH, |
2091 | 32 | PASSWORD_MATCH, | 38 | PASSWORD_MATCH, |
2093 | 33 | PASSWORD_UPPER) | 39 | PASSWORD_UPPER, |
2094 | 40 | ) | ||
2095 | 41 | from ubuntu_sso.qt.tests import build_string_for_pixels | ||
2096 | 34 | 42 | ||
2097 | 35 | 43 | ||
2098 | 36 | class PasswordTestCase(TestCase): | 44 | class PasswordTestCase(TestCase): |
2099 | @@ -234,3 +242,123 @@ | |||
2100 | 234 | line_edit = QtGui.QLineEdit() | 242 | line_edit = QtGui.QLineEdit() |
2101 | 235 | check_as_invalid(line_edit) | 243 | check_as_invalid(line_edit) |
2102 | 236 | self.assertTrue(line_edit.property("formError").toBool()) | 244 | self.assertTrue(line_edit.property("formError").toBool()) |
2103 | 245 | |||
2104 | 246 | |||
2105 | 247 | class ElidedTextTestCase(TestCase): | ||
2106 | 248 | """The test case for the maybe_elide_text function.""" | ||
2107 | 249 | |||
2108 | 250 | max_width = 100 | ||
2109 | 251 | |||
2110 | 252 | @defer.inlineCallbacks | ||
2111 | 253 | def setUp(self): | ||
2112 | 254 | """Setup tests.""" | ||
2113 | 255 | yield super(ElidedTextTestCase, self).setUp() | ||
2114 | 256 | self.ui = QtGui.QLabel() | ||
2115 | 257 | |||
2116 | 258 | def test_text_not_elided_if_too_short(self): | ||
2117 | 259 | """If text is shorter than max_width, do not elide.""" | ||
2118 | 260 | text = build_string_for_pixels(self.ui, self.max_width - 1) | ||
2119 | 261 | |||
2120 | 262 | maybe_elide_text(self.ui, text, self.max_width) | ||
2121 | 263 | |||
2122 | 264 | self.assertEqual(self.ui.toolTip(), '') | ||
2123 | 265 | self.assertEqual(self.ui.text(), text) | ||
2124 | 266 | self.assertNotIn(u'\u2026', self.ui.text()) | ||
2125 | 267 | |||
2126 | 268 | def test_text_not_elided_if_equals_max_width(self): | ||
2127 | 269 | """If text is equal than max_width, do not elide.""" | ||
2128 | 270 | text = build_string_for_pixels(self.ui, self.max_width) | ||
2129 | 271 | |||
2130 | 272 | maybe_elide_text(self.ui, text, self.max_width) | ||
2131 | 273 | |||
2132 | 274 | self.assertEqual(self.ui.toolTip(), '') | ||
2133 | 275 | self.assertEqual(self.ui.text(), text) | ||
2134 | 276 | self.assertNotIn(u'\u2026', self.ui.text()) | ||
2135 | 277 | |||
2136 | 278 | def test_text_elided_if_bigger_than_max_width(self): | ||
2137 | 279 | """If text is equal than max_width, do not elide.""" | ||
2138 | 280 | text = build_string_for_pixels(self.ui, self.max_width + 10) | ||
2139 | 281 | |||
2140 | 282 | maybe_elide_text(self.ui, text, self.max_width) | ||
2141 | 283 | |||
2142 | 284 | self.assertEqual(self.ui.toolTip(), text) | ||
2143 | 285 | expected = unicode(self.ui.text()) | ||
2144 | 286 | self.assertTrue(expected.endswith(u'\u2026')) | ||
2145 | 287 | self.assertTrue(text.startswith(expected[:-1])) | ||
2146 | 288 | |||
2147 | 289 | |||
2148 | 290 | class BuildGeneralErrorMessageTestCase(TestCase): | ||
2149 | 291 | """Test passwords conditions.""" | ||
2150 | 292 | |||
2151 | 293 | def test_with_message(self): | ||
2152 | 294 | """Test build_general_error_message with 'message' key.""" | ||
2153 | 295 | error = "error message" | ||
2154 | 296 | err_dict = {'message': error} | ||
2155 | 297 | |||
2156 | 298 | result = build_general_error_message(err_dict) | ||
2157 | 299 | |||
2158 | 300 | self.assertEqual(result, error) | ||
2159 | 301 | |||
2160 | 302 | def test_with_all(self): | ||
2161 | 303 | """Test build_general_error_message with 'all' key.""" | ||
2162 | 304 | error = "error message" | ||
2163 | 305 | err_dict = {'__all__': error} | ||
2164 | 306 | |||
2165 | 307 | result = build_general_error_message(err_dict) | ||
2166 | 308 | |||
2167 | 309 | self.assertEqual(result, error) | ||
2168 | 310 | |||
2169 | 311 | def test_with_message_and_all(self): | ||
2170 | 312 | """Test build_general_error_message with 'all' and 'message' key.""" | ||
2171 | 313 | error = "error message" | ||
2172 | 314 | error2 = "error message2" | ||
2173 | 315 | err_dict = {'__all__': error, 'message': error2} | ||
2174 | 316 | |||
2175 | 317 | result = build_general_error_message(err_dict) | ||
2176 | 318 | |||
2177 | 319 | expected = '\n'.join((error, error2)) | ||
2178 | 320 | self.assertEqual(result, expected) | ||
2179 | 321 | |||
2180 | 322 | def test_with_all_and_error_message(self): | ||
2181 | 323 | """Test for 'all' and 'error_message' key.""" | ||
2182 | 324 | error = "error message" | ||
2183 | 325 | error2 = "error message2" | ||
2184 | 326 | err_dict = {'__all__': error, 'error_message': error2} | ||
2185 | 327 | result = build_general_error_message(err_dict) | ||
2186 | 328 | expected = '\n'.join((error, error2)) | ||
2187 | 329 | self.assertEqual(result, expected) | ||
2188 | 330 | |||
2189 | 331 | def test_with_random_keys(self): | ||
2190 | 332 | """Test build_general_error_message with random keys.""" | ||
2191 | 333 | error = "error message" | ||
2192 | 334 | error2 = "error message2" | ||
2193 | 335 | err_dict = {'my_bad': error, 'odd_error': error2} | ||
2194 | 336 | |||
2195 | 337 | result = build_general_error_message(err_dict) | ||
2196 | 338 | |||
2197 | 339 | expected = '\n'.join( | ||
2198 | 340 | [('%s: %s' % (k, v)) for k, v in err_dict.iteritems()]) | ||
2199 | 341 | self.assertEqual(result, expected) | ||
2200 | 342 | |||
2201 | 343 | def test_with_random_keys_with_errtype(self): | ||
2202 | 344 | """Test build_general_error_message with random keys and errtype.""" | ||
2203 | 345 | error = "error message" | ||
2204 | 346 | error2 = "error message2" | ||
2205 | 347 | err_dict = {'my_bad': error, 'odd_error': error2, 'errtype': 'Danger'} | ||
2206 | 348 | |||
2207 | 349 | result = build_general_error_message(err_dict) | ||
2208 | 350 | |||
2209 | 351 | expected = '\n'.join( | ||
2210 | 352 | [('%s: %s' % (k, v)) \ | ||
2211 | 353 | for k, v in {'my_bad': error, 'odd_error': error2}.iteritems()]) | ||
2212 | 354 | self.assertEqual(result, expected) | ||
2213 | 355 | |||
2214 | 356 | def test_with_not_dict(self): | ||
2215 | 357 | """Test build_general_error_message with argument not dict.""" | ||
2216 | 358 | error = "error message" | ||
2217 | 359 | err_dict = Exception(error) | ||
2218 | 360 | |||
2219 | 361 | result = build_general_error_message(err_dict) | ||
2220 | 362 | |||
2221 | 363 | expected = GENERIC_BACKEND_ERROR | ||
2222 | 364 | self.assertEqual(result, expected) | ||
2223 | 237 | 365 | ||
2224 | === modified file 'ubuntu_sso/qt/tests/test_current_user_sign_in_page.py' | |||
2225 | --- ubuntu_sso/qt/tests/test_current_user_sign_in_page.py 2012-03-05 20:30:57 +0000 | |||
2226 | +++ ubuntu_sso/qt/tests/test_current_user_sign_in_page.py 2012-03-20 16:10:09 +0000 | |||
2227 | @@ -24,6 +24,7 @@ | |||
2228 | 24 | FakePageUiStyle, | 24 | FakePageUiStyle, |
2229 | 25 | FakeWizardButtonStyle, | 25 | FakeWizardButtonStyle, |
2230 | 26 | ) | 26 | ) |
2231 | 27 | from ubuntu_sso.tests import EMAIL | ||
2232 | 27 | 28 | ||
2233 | 28 | 29 | ||
2234 | 29 | # pylint: disable=W0212 | 30 | # pylint: disable=W0212 |
2235 | @@ -51,19 +52,32 @@ | |||
2236 | 51 | self.assertTrue(button.properties['default']) | 52 | self.assertTrue(button.properties['default']) |
2237 | 52 | self.assertFalse(button.isEnabled()) | 53 | self.assertFalse(button.isEnabled()) |
2238 | 53 | 54 | ||
2239 | 55 | def test_unicode_in_forgotten_password_link(self): | ||
2240 | 56 | """Ensure that this label supports unicode.""" | ||
2241 | 57 | forgot_fr = u"J'ai oublié mon mot de passe" | ||
2242 | 58 | self.patch(gui, "FORGOTTEN_PASSWORD_BUTTON", forgot_fr) | ||
2243 | 59 | forgotten_text = gui.LINK_STYLE.format(link_url='#', | ||
2244 | 60 | link_text=forgot_fr) | ||
2245 | 61 | self.ui._set_translated_strings() | ||
2246 | 62 | self.assertEqual(unicode(self.ui.ui.forgot_password_label.text()), | ||
2247 | 63 | forgotten_text) | ||
2248 | 64 | |||
2249 | 54 | def test_set_translated_strings(self): | 65 | def test_set_translated_strings(self): |
2250 | 55 | """Test the translated string method.""" | 66 | """Test the translated string method.""" |
2251 | 56 | expected = gui.LOGIN_TITLE.format(app_name=self.app_name) | 67 | expected = gui.LOGIN_TITLE.format(app_name=self.app_name) |
2252 | 57 | self.assert_title_correct(expected) | 68 | self.assert_title_correct(expected) |
2253 | 58 | expected = gui.LOGIN_SUBTITLE % dict(app_name=self.app_name) | 69 | expected = gui.LOGIN_SUBTITLE % dict(app_name=self.app_name) |
2254 | 59 | self.assert_subtitle_correct(expected) | 70 | self.assert_subtitle_correct(expected) |
2257 | 60 | self.assertEqual(self.ui.ui.email_label.text(), gui.EMAIL_LABEL) | 71 | self.assertEqual(unicode(self.ui.ui.email_label.text()), |
2258 | 61 | self.assertEqual(self.ui.ui.password_label.text(), | 72 | gui.EMAIL_LABEL) |
2259 | 73 | self.assertEqual(unicode(self.ui.ui.password_label.text()), | ||
2260 | 62 | gui.LOGIN_PASSWORD_LABEL) | 74 | gui.LOGIN_PASSWORD_LABEL) |
2261 | 63 | text = gui.LINK_STYLE.format(link_url='#', | 75 | text = gui.LINK_STYLE.format(link_url='#', |
2262 | 64 | link_text=gui.FORGOTTEN_PASSWORD_BUTTON) | 76 | link_text=gui.FORGOTTEN_PASSWORD_BUTTON) |
2265 | 65 | self.assertEqual(self.ui.ui.forgot_password_label.text(), text) | 77 | self.assertEqual(unicode(self.ui.ui.forgot_password_label.text()), |
2266 | 66 | self.assertEqual(self.ui.ui.sign_in_button.text(), gui.SIGN_IN_BUTTON) | 78 | text) |
2267 | 79 | self.assertEqual(unicode(self.ui.ui.sign_in_button.text()), | ||
2268 | 80 | gui.SIGN_IN_BUTTON) | ||
2269 | 67 | 81 | ||
2270 | 68 | def test_connect_ui(self): | 82 | def test_connect_ui(self): |
2271 | 69 | """Test the connect ui method.""" | 83 | """Test the connect ui method.""" |
2272 | @@ -128,30 +142,27 @@ | |||
2273 | 128 | def test_on_login_error(self): | 142 | def test_on_login_error(self): |
2274 | 129 | """Test the on_login_error method.""" | 143 | """Test the on_login_error method.""" |
2275 | 130 | self.patch(self.ui, "show_error", self._set_called) | 144 | self.patch(self.ui, "show_error", self._set_called) |
2276 | 131 | app_name = 'my_app' | ||
2277 | 132 | self.ui.app_name = app_name | ||
2278 | 133 | error = {'errtype': 'UserNotValidated'} | 145 | error = {'errtype': 'UserNotValidated'} |
2280 | 134 | self.ui.on_login_error(app_name, error) | 146 | |
2281 | 147 | self.ui.on_login_error(self.app_name, error) | ||
2282 | 148 | |||
2283 | 135 | self.assertEqual(self._overlay_hide_counter, 0) | 149 | self.assertEqual(self._overlay_hide_counter, 0) |
2284 | 136 | self.assertTrue(self.ui.isEnabled()) | 150 | self.assertTrue(self.ui.isEnabled()) |
2286 | 137 | expected = ((self.ui, 'my_app', ''), {}) | 151 | expected = ((self.ui, self.app_name, ''), {}) |
2287 | 138 | self.assertTrue(expected, self._called) | 152 | self.assertTrue(expected, self._called) |
2288 | 139 | 153 | ||
2289 | 140 | def test_on_logged_in(self): | 154 | def test_on_logged_in(self): |
2290 | 141 | """Test the on_login_in method.""" | 155 | """Test the on_login_in method.""" |
2296 | 142 | email = 'email@example' | 156 | self.ui.ui.email_edit.setText(EMAIL) |
2297 | 143 | self.ui.ui.email_edit.setText(email) | 157 | self.assert_signal_emitted(self.ui.userLoggedIn, (EMAIL,), |
2298 | 144 | 158 | self.ui.on_logged_in, self.app_name, EMAIL) | |
2294 | 145 | self.assert_signal_emitted(self.ui.userLoggedIn, (email,), | ||
2295 | 146 | self.ui.on_logged_in, self.app_name, email) | ||
2299 | 147 | self.assertTrue(self.ui.isEnabled()) | 159 | self.assertTrue(self.ui.isEnabled()) |
2300 | 148 | 160 | ||
2301 | 149 | def test_on_user_not_validated(self): | 161 | def test_on_user_not_validated(self): |
2302 | 150 | """Test the navigation flow on user not validated.""" | 162 | """Test the navigation flow on user not validated.""" |
2307 | 151 | email = 'email@example' | 163 | self.ui.ui.email_edit.setText(EMAIL) |
2308 | 152 | self.ui.ui.email_edit.setText(email) | 164 | self.assert_signal_emitted(self.ui.userNotValidated, (EMAIL,), |
2309 | 153 | self.assert_signal_emitted(self.ui.userNotValidated, (email,), | 165 | self.ui.on_user_not_validated, self.app_name, EMAIL) |
2306 | 154 | self.ui.on_user_not_validated, self.app_name, email) | ||
2310 | 155 | 166 | ||
2311 | 156 | def test_on_forgotten_password(self): | 167 | def test_on_forgotten_password(self): |
2312 | 157 | """Test the on_forgotten_password method.""" | 168 | """Test the on_forgotten_password method.""" |
2313 | 158 | 169 | ||
2314 | === modified file 'ubuntu_sso/qt/tests/test_email_verification.py' | |||
2315 | --- ubuntu_sso/qt/tests/test_email_verification.py 2012-03-05 18:56:50 +0000 | |||
2316 | +++ ubuntu_sso/qt/tests/test_email_verification.py 2012-03-20 16:10:09 +0000 | |||
2317 | @@ -78,14 +78,12 @@ | |||
2318 | 78 | email = 'mail@example' | 78 | email = 'mail@example' |
2319 | 79 | self.ui.email = email | 79 | self.ui.email = email |
2320 | 80 | self.ui.set_titles(email) | 80 | self.ui.set_titles(email) |
2323 | 81 | self.assertEqual(self.ui.header.title_label.text(), | 81 | self.assert_title_correct(email_verification_page.VERIFY_EMAIL_TITLE) |
2322 | 82 | email_verification_page.VERIFY_EMAIL_TITLE) | ||
2324 | 83 | expected = email_verification_page.VERIFY_EMAIL_CONTENT % { | 82 | expected = email_verification_page.VERIFY_EMAIL_CONTENT % { |
2325 | 84 | "app_name": self.app_name, | 83 | "app_name": self.app_name, |
2326 | 85 | "email": email, | 84 | "email": email, |
2327 | 86 | } | 85 | } |
2330 | 87 | self.assertEqual(unicode(self.ui.header.subtitle_label.toolTip()), | 86 | self.assert_subtitle_correct(expected) |
2329 | 88 | expected) | ||
2331 | 89 | 87 | ||
2332 | 90 | def test_initialize_page(self): | 88 | def test_initialize_page(self): |
2333 | 91 | """Test the initialization method.""" | 89 | """Test the initialization method.""" |
2334 | @@ -100,10 +98,11 @@ | |||
2335 | 100 | def test_on_email_validation_error(self): | 98 | def test_on_email_validation_error(self): |
2336 | 101 | """Test the validate_email method.""" | 99 | """Test the validate_email method.""" |
2337 | 102 | self.patch(self.ui, "show_error", self._set_called) | 100 | self.patch(self.ui, "show_error", self._set_called) |
2338 | 103 | app_name = 'my_app' | ||
2339 | 104 | error = {email_verification_page: 'error in email_verification_page'} | 101 | error = {email_verification_page: 'error in email_verification_page'} |
2342 | 105 | self.ui.on_email_validation_error(app_name, error) | 102 | |
2343 | 106 | expected = ((self.ui, app_name, ''), {}) | 103 | self.ui.on_email_validation_error(self.app_name, error) |
2344 | 104 | |||
2345 | 105 | expected = ((self.ui, ''), {}) | ||
2346 | 107 | self.assertTrue(expected, self._called) | 106 | self.assertTrue(expected, self._called) |
2347 | 108 | self.assertEqual(self._overlay_hide_counter, 1) | 107 | self.assertEqual(self._overlay_hide_counter, 1) |
2348 | 109 | 108 | ||
2349 | 110 | 109 | ||
2350 | === modified file 'ubuntu_sso/qt/tests/test_enhanced_check_box.py' | |||
2351 | --- ubuntu_sso/qt/tests/test_enhanced_check_box.py 2012-02-16 18:40:41 +0000 | |||
2352 | +++ ubuntu_sso/qt/tests/test_enhanced_check_box.py 2012-03-20 16:10:09 +0000 | |||
2353 | @@ -16,7 +16,7 @@ | |||
2354 | 16 | 16 | ||
2355 | 17 | """Tests for the EnhancedCheckBox widget.""" | 17 | """Tests for the EnhancedCheckBox widget.""" |
2356 | 18 | 18 | ||
2358 | 19 | from PyQt4 import QtCore | 19 | from PyQt4 import QtGui, QtCore |
2359 | 20 | 20 | ||
2360 | 21 | from ubuntu_sso.qt import enhanced_check_box | 21 | from ubuntu_sso.qt import enhanced_check_box |
2361 | 22 | from ubuntu_sso.qt.tests import BaseTestCase | 22 | from ubuntu_sso.qt.tests import BaseTestCase |
2362 | @@ -40,3 +40,21 @@ | |||
2363 | 40 | check.setText("text") | 40 | check.setText("text") |
2364 | 41 | self.assertEqual(check.text(), "text") | 41 | self.assertEqual(check.text(), "text") |
2365 | 42 | self.assertEqual(check.text(), check.text_label.text()) | 42 | self.assertEqual(check.text(), check.text_label.text()) |
2366 | 43 | |||
2367 | 44 | def test_enhanced_check_size_adjust_with_small_height(self): | ||
2368 | 45 | """Check if the size of the EnhancedCheckBox is adjusted correctly.""" | ||
2369 | 46 | text = 't' * 200 | ||
2370 | 47 | height = 10 | ||
2371 | 48 | widget = QtGui.QWidget() | ||
2372 | 49 | widget.setFixedSize(200, height) | ||
2373 | 50 | check = enhanced_check_box.EnhancedCheckBox(text, widget) | ||
2374 | 51 | self.assertTrue(check.height() > height) | ||
2375 | 52 | |||
2376 | 53 | def test_enhanced_check_size_adjust_with_big_height(self): | ||
2377 | 54 | """Check if the size of the EnhancedCheckBox is adjusted correctly.""" | ||
2378 | 55 | text = 't' * 20 | ||
2379 | 56 | height = 200 | ||
2380 | 57 | widget = QtGui.QWidget() | ||
2381 | 58 | widget.setFixedSize(200, height) | ||
2382 | 59 | check = enhanced_check_box.EnhancedCheckBox(text, widget) | ||
2383 | 60 | self.assertTrue(check.height() < height) | ||
2384 | 43 | 61 | ||
2385 | === modified file 'ubuntu_sso/qt/tests/test_forgotten_password.py' | |||
2386 | --- ubuntu_sso/qt/tests/test_forgotten_password.py 2012-03-05 20:30:57 +0000 | |||
2387 | +++ ubuntu_sso/qt/tests/test_forgotten_password.py 2012-03-20 16:10:09 +0000 | |||
2388 | @@ -75,9 +75,10 @@ | |||
2389 | 75 | subtitle = gui.FORGOTTEN_PASSWORD_SUBTITLE | 75 | subtitle = gui.FORGOTTEN_PASSWORD_SUBTITLE |
2390 | 76 | self.assert_subtitle_correct(subtitle.format(app_name=self.app_name)) | 76 | self.assert_subtitle_correct(subtitle.format(app_name=self.app_name)) |
2391 | 77 | 77 | ||
2393 | 78 | self.assertEqual(self.ui.ui.email_address_label.text(), | 78 | self.assertEqual(unicode(self.ui.ui.email_address_label.text()), |
2394 | 79 | gui.EMAIL_LABEL) | 79 | gui.EMAIL_LABEL) |
2396 | 80 | self.assertEqual(self.ui.ui.send_button.text(), gui.RESET_PASSWORD) | 80 | self.assertEqual(unicode(self.ui.ui.send_button.text()), |
2397 | 81 | gui.RESET_PASSWORD) | ||
2398 | 81 | 82 | ||
2399 | 82 | def test_connect_ui(self): | 83 | def test_connect_ui(self): |
2400 | 83 | """Test the connect ui method.""" | 84 | """Test the connect ui method.""" |
2401 | @@ -112,7 +113,8 @@ | |||
2402 | 112 | """Test on_password_reset_error method.""" | 113 | """Test on_password_reset_error method.""" |
2403 | 113 | self.patch(self.ui, "show_error", self._set_called) | 114 | self.patch(self.ui, "show_error", self._set_called) |
2404 | 114 | error = {'errtype': 'FooBarBaz'} | 115 | error = {'errtype': 'FooBarBaz'} |
2405 | 116 | |||
2406 | 115 | self.ui.on_password_reset_error(self.app_name, error) | 117 | self.ui.on_password_reset_error(self.app_name, error) |
2409 | 116 | expected = ((self.ui, self.app_name, | 118 | |
2410 | 117 | gui.REQUEST_PASSWORD_TOKEN_WRONG_EMAIL), {}) | 119 | expected = ((self.ui, gui.REQUEST_PASSWORD_TOKEN_WRONG_EMAIL), {}) |
2411 | 118 | self.assertTrue(expected, self._called) | 120 | self.assertTrue(expected, self._called) |
2412 | 119 | 121 | ||
2413 | === modified file 'ubuntu_sso/qt/tests/test_loadingoverlay.py' | |||
2414 | --- ubuntu_sso/qt/tests/test_loadingoverlay.py 2012-02-23 19:49:02 +0000 | |||
2415 | +++ ubuntu_sso/qt/tests/test_loadingoverlay.py 2012-03-20 16:10:09 +0000 | |||
2416 | @@ -29,8 +29,5 @@ | |||
2417 | 29 | 29 | ||
2418 | 30 | def test_status_correct(self): | 30 | def test_status_correct(self): |
2419 | 31 | """Test if the necessary variables for the animation exists""" | 31 | """Test if the necessary variables for the animation exists""" |
2420 | 32 | self.ui.show() | ||
2421 | 33 | self.addCleanup(self.ui.hide) | ||
2422 | 34 | |||
2423 | 35 | self.assertTrue(self.ui.counter is not None) | 32 | self.assertTrue(self.ui.counter is not None) |
2424 | 36 | self.assertTrue(self.ui.orientation is not None) | 33 | self.assertTrue(self.ui.orientation is not None) |
2425 | 37 | 34 | ||
2426 | === modified file 'ubuntu_sso/qt/tests/test_main.py' | |||
2427 | --- ubuntu_sso/qt/tests/test_main.py 2012-02-13 15:43:59 +0000 | |||
2428 | +++ ubuntu_sso/qt/tests/test_main.py 2012-03-20 16:10:09 +0000 | |||
2429 | @@ -16,27 +16,123 @@ | |||
2430 | 16 | 16 | ||
2431 | 17 | """Tests for the main module.""" | 17 | """Tests for the main module.""" |
2432 | 18 | 18 | ||
2433 | 19 | from PyQt4 import QtCore | ||
2434 | 20 | from twisted.internet import defer | ||
2435 | 19 | from twisted.trial.unittest import TestCase | 21 | from twisted.trial.unittest import TestCase |
2436 | 20 | 22 | ||
2437 | 21 | from ubuntu_sso.qt import main | 23 | from ubuntu_sso.qt import main |
2438 | 24 | from ubuntu_sso import tests | ||
2439 | 25 | |||
2440 | 26 | |||
2441 | 27 | # pylint: disable=C0103 | ||
2442 | 28 | class FakeUi(object): | ||
2443 | 29 | |||
2444 | 30 | """A fake UI.""" | ||
2445 | 31 | |||
2446 | 32 | def size(self): | ||
2447 | 33 | """Fake size.""" | ||
2448 | 34 | return QtCore.QSize(100, 100) | ||
2449 | 35 | |||
2450 | 36 | def setGeometry(self, *args): | ||
2451 | 37 | """Fake setGeometry.""" | ||
2452 | 38 | |||
2453 | 39 | show = setGeometry | ||
2454 | 40 | |||
2455 | 41 | |||
2456 | 42 | class FakeDesktop(object): | ||
2457 | 43 | |||
2458 | 44 | """Fake Desktop Widget.""" | ||
2459 | 45 | |||
2460 | 46 | def availableGeometry(self): | ||
2461 | 47 | """Fake availableGeometry for desktop.-""" | ||
2462 | 48 | return QtCore.QRect(100, 100, 100, 100) | ||
2463 | 49 | |||
2464 | 50 | |||
2465 | 51 | class FakeApplication(object): | ||
2466 | 52 | |||
2467 | 53 | """Fake QApplication.""" | ||
2468 | 54 | |||
2469 | 55 | called = {} | ||
2470 | 56 | __instance = None | ||
2471 | 57 | |||
2472 | 58 | def __init__(self, args): | ||
2473 | 59 | self.called['args'] = args | ||
2474 | 60 | FakeApplication.__instance = self | ||
2475 | 61 | |||
2476 | 62 | def setStyleSheet(self, style): | ||
2477 | 63 | """Fake setStyleSheet.""" | ||
2478 | 64 | self.called["setStyleSheet"] = style | ||
2479 | 65 | |||
2480 | 66 | def styleSheet(self): | ||
2481 | 67 | """Fake get style sheet.""" | ||
2482 | 68 | return self.called.get("setStyleSheet", '') | ||
2483 | 69 | |||
2484 | 70 | def desktop(self): | ||
2485 | 71 | """Fake Desktop.""" | ||
2486 | 72 | return FakeDesktop() | ||
2487 | 73 | |||
2488 | 74 | def exec_(self): | ||
2489 | 75 | """Fake exec_.""" | ||
2490 | 76 | |||
2491 | 77 | def exit(self): | ||
2492 | 78 | """Fake exit.""" | ||
2493 | 79 | |||
2494 | 80 | @classmethod | ||
2495 | 81 | def instance(cls): | ||
2496 | 82 | """Fake instance.""" | ||
2497 | 83 | return FakeApplication.__instance | ||
2498 | 84 | # pylint: enable=C0103 | ||
2499 | 22 | 85 | ||
2500 | 23 | 86 | ||
2501 | 24 | class BasicTestCase(TestCase): | 87 | class BasicTestCase(TestCase): |
2502 | 25 | """Test case with a helper tracker.""" | 88 | """Test case with a helper tracker.""" |
2503 | 26 | 89 | ||
2504 | 90 | @defer.inlineCallbacks | ||
2505 | 91 | def setUp(self): | ||
2506 | 92 | yield super(BasicTestCase, self).setUp() | ||
2507 | 93 | self.called = [] | ||
2508 | 94 | |||
2509 | 95 | def called_ui(**kw): | ||
2510 | 96 | """record ui call.""" | ||
2511 | 97 | self.called.append(('GUI', kw)) | ||
2512 | 98 | return FakeUi() | ||
2513 | 99 | |||
2514 | 100 | self.patch(main, 'UbuntuSSOClientGUI', called_ui) | ||
2515 | 101 | self.patch(main.QtGui, 'QApplication', FakeApplication) | ||
2516 | 102 | |||
2517 | 27 | def test_main(self): | 103 | def test_main(self): |
2518 | 28 | """Calling main.main() a UI instance is created.""" | 104 | """Calling main.main() a UI instance is created.""" |
2525 | 29 | called = [] | 105 | kwargs = dict(app_name='APP_NAME', foo='foo', bar='bar', |
2526 | 30 | self.patch(main, 'UbuntuSSOClientGUI', | 106 | baz='yadda', yadda=0) |
2527 | 31 | lambda **kw: called.append(('GUI', kw))) | 107 | main.main(**kwargs) |
2528 | 32 | self.patch(main.QtGui.QApplication, 'exec_', | 108 | |
2529 | 33 | lambda *a: called.append('main')) | 109 | kwargs['close_callback'] = main.QtGui.QApplication.instance().exit |
2530 | 34 | 110 | self.assertEqual(self.called, [('GUI', kwargs)]) | |
2531 | 111 | |||
2532 | 112 | def test_main_args(self): | ||
2533 | 113 | """Calling main.main() a UI instance is created.""" | ||
2534 | 114 | arg_list = (tests.APP_NAME, tests.NAME, tests.PASSWORD, | ||
2535 | 115 | tests.EMAIL_TOKEN) | ||
2536 | 116 | kwargs = dict(app_name=arg_list[0].encode('utf-8'), | ||
2537 | 117 | foo=arg_list[1].encode('utf-8'), | ||
2538 | 118 | bar=arg_list[2].encode('utf-8'), | ||
2539 | 119 | baz=arg_list[3].encode('utf-8')) | ||
2540 | 120 | main.main(**kwargs) | ||
2541 | 121 | |||
2542 | 122 | kwargs['close_callback'] = main.QtGui.QApplication.instance().exit | ||
2543 | 123 | expected = dict(app_name=arg_list[0], foo=arg_list[1], | ||
2544 | 124 | bar=arg_list[2], baz=arg_list[3], | ||
2545 | 125 | close_callback=main.QtGui.QApplication.instance().exit) | ||
2546 | 126 | self.assertEqual(self.called, [('GUI', expected)]) | ||
2547 | 127 | |||
2548 | 128 | def test_styles_load(self): | ||
2549 | 129 | """Test that all stylesheets load.""" | ||
2550 | 35 | kwargs = dict(foo='foo', bar='bar', baz='yadda', yadda=0) | 130 | kwargs = dict(foo='foo', bar='bar', baz='yadda', yadda=0) |
2551 | 36 | main.main(**kwargs) | 131 | main.main(**kwargs) |
2558 | 37 | 132 | data = [] | |
2559 | 38 | kwargs['close_callback'] = main.QtGui.QApplication.instance().exit | 133 | for qss_name in (main.PLATFORM_QSS, ":/stylesheet.qss"): |
2560 | 39 | self.assertEqual(called, [('GUI', kwargs), 'main']) | 134 | qss = QtCore.QResource(qss_name) |
2561 | 40 | 135 | data.append(unicode(qss.data())) | |
2562 | 41 | test_main.skip = 'Failing with QObject::startTimer: QTimer can only be ' \ | 136 | self.assertEqual( |
2563 | 42 | 'used with threads started with QThread' | 137 | unicode(main.QtGui.QApplication.instance().styleSheet()), |
2564 | 138 | '\n'.join(data)) | ||
2565 | 43 | 139 | ||
2566 | === modified file 'ubuntu_sso/qt/tests/test_proxy_dialog.py' | |||
2567 | --- ubuntu_sso/qt/tests/test_proxy_dialog.py 2012-02-13 16:16:03 +0000 | |||
2568 | +++ ubuntu_sso/qt/tests/test_proxy_dialog.py 2012-03-20 16:10:09 +0000 | |||
2569 | @@ -256,7 +256,7 @@ | |||
2570 | 256 | self.patch(dialog, 'done', fake_done) | 256 | self.patch(dialog, 'done', fake_done) |
2571 | 257 | 257 | ||
2572 | 258 | dialog._on_cancel_clicked() | 258 | dialog._on_cancel_clicked() |
2574 | 259 | self.assertIn(('done', proxy_dialog.USER_CANCELATION), called) | 259 | self.assertIn(('done', proxy_dialog.USER_CANCELLATION), called) |
2575 | 260 | 260 | ||
2576 | 261 | def assert_save_button(self, set_creds_callback, result_number): | 261 | def assert_save_button(self, set_creds_callback, result_number): |
2577 | 262 | """Test the save button execution.""" | 262 | """Test the save button execution.""" |
2578 | @@ -288,7 +288,7 @@ | |||
2579 | 288 | def test_on_save_clicked_correct(self): | 288 | def test_on_save_clicked_correct(self): |
2580 | 289 | """Test that we do save the creds.""" | 289 | """Test that we do save the creds.""" |
2581 | 290 | set_creds_cb = lambda: defer.succeed(True) | 290 | set_creds_cb = lambda: defer.succeed(True) |
2583 | 291 | result_number = proxy_dialog.CREDS_ACQUIRED | 291 | result_number = proxy_dialog.USER_SUCCESS |
2584 | 292 | self.assert_save_button(set_creds_cb, result_number) | 292 | self.assert_save_button(set_creds_cb, result_number) |
2585 | 293 | 293 | ||
2586 | 294 | 294 | ||
2587 | 295 | 295 | ||
2588 | === modified file 'ubuntu_sso/qt/tests/test_reset_password.py' | |||
2589 | --- ubuntu_sso/qt/tests/test_reset_password.py 2012-03-05 18:56:50 +0000 | |||
2590 | +++ ubuntu_sso/qt/tests/test_reset_password.py 2012-03-20 16:10:09 +0000 | |||
2591 | @@ -70,27 +70,23 @@ | |||
2592 | 70 | 70 | ||
2593 | 71 | def test_initialize(self): | 71 | def test_initialize(self): |
2594 | 72 | """Check the Title and Subtitle.""" | 72 | """Check the Title and Subtitle.""" |
2595 | 73 | self.ui.show() | ||
2596 | 74 | self.ui.initializePage() | 73 | self.ui.initializePage() |
2597 | 75 | self.addCleanup(self.ui.hide) | ||
2598 | 76 | self.assert_title_correct(RESET_TITLE) | 74 | self.assert_title_correct(RESET_TITLE) |
2599 | 77 | self.assert_subtitle_correct(RESET_SUBTITLE) | 75 | self.assert_subtitle_correct(RESET_SUBTITLE) |
2602 | 78 | self.assertEqual(self.ui.ui.password_label.text(), PASSWORD1_ENTRY) | 76 | self.assertEqual(unicode(self.ui.ui.password_label.text()), |
2603 | 79 | self.assertEqual(self.ui.ui.confirm_password_label.text(), | 77 | PASSWORD1_ENTRY) |
2604 | 78 | self.assertEqual(unicode(self.ui.ui.confirm_password_label.text()), | ||
2605 | 80 | PASSWORD2_ENTRY) | 79 | PASSWORD2_ENTRY) |
2607 | 81 | self.assertEqual(self.ui.ui.reset_code.text(), RESET_CODE_ENTRY) | 80 | self.assertEqual(unicode(self.ui.ui.reset_code.text()), |
2608 | 81 | RESET_CODE_ENTRY) | ||
2609 | 82 | 82 | ||
2610 | 83 | def test_focus_changed_password_visibility(self): | 83 | def test_focus_changed_password_visibility(self): |
2611 | 84 | """Check visibility changes when focus_changed() is executed.""" | 84 | """Check visibility changes when focus_changed() is executed.""" |
2612 | 85 | self.ui.show() | ||
2613 | 86 | self.addCleanup(self.ui.hide) | ||
2614 | 87 | self.ui.focus_changed(None, self.ui.ui.password_line_edit) | 85 | self.ui.focus_changed(None, self.ui.ui.password_line_edit) |
2615 | 88 | self.assertTrue(self.ui.ui.password_assistance.isVisible()) | 86 | self.assertTrue(self.ui.ui.password_assistance.isVisible()) |
2616 | 89 | 87 | ||
2617 | 90 | def test_show_hide_event(self): | 88 | def test_show_hide_event(self): |
2618 | 91 | """Check connections to focusChanged on show and hide event.""" | 89 | """Check connections to focusChanged on show and hide event.""" |
2619 | 92 | self.ui.show() | ||
2620 | 93 | self.addCleanup(self.ui.hide) | ||
2621 | 94 | self.assertEqual(QtGui.QApplication.instance().receivers( | 90 | self.assertEqual(QtGui.QApplication.instance().receivers( |
2622 | 95 | QtCore.SIGNAL("focusChanged(QWidget*, QWidget*)")), 1) | 91 | QtCore.SIGNAL("focusChanged(QWidget*, QWidget*)")), 1) |
2623 | 96 | self.ui.hide() | 92 | self.ui.hide() |
2624 | @@ -102,24 +98,20 @@ | |||
2625 | 102 | def test_focus_changed_1(self): | 98 | def test_focus_changed_1(self): |
2626 | 103 | """Check functions execution when focus_changed() is executed.""" | 99 | """Check functions execution when focus_changed() is executed.""" |
2627 | 104 | self.patch(common, 'password_default_assistance', self._set_called) | 100 | self.patch(common, 'password_default_assistance', self._set_called) |
2628 | 105 | |||
2629 | 106 | self.ui.show() | ||
2630 | 107 | self.addCleanup(self.ui.hide) | ||
2631 | 108 | |||
2632 | 109 | self.assertFalse(self._called) | 101 | self.assertFalse(self._called) |
2633 | 102 | |||
2634 | 110 | self.ui.focus_changed(None, self.ui.ui.password_line_edit) | 103 | self.ui.focus_changed(None, self.ui.ui.password_line_edit) |
2635 | 104 | |||
2636 | 111 | self.assertTrue(self.ui.ui.password_assistance.isVisible()) | 105 | self.assertTrue(self.ui.ui.password_assistance.isVisible()) |
2637 | 112 | self.assertTrue(self._called) | 106 | self.assertTrue(self._called) |
2638 | 113 | 107 | ||
2639 | 114 | def test_focus_changed_2(self): | 108 | def test_focus_changed_2(self): |
2640 | 115 | """Check functions execution when focus_changed() is executed.""" | 109 | """Check functions execution when focus_changed() is executed.""" |
2641 | 116 | self.patch(common, 'password_check_match', self._set_called) | 110 | self.patch(common, 'password_check_match', self._set_called) |
2642 | 117 | |||
2643 | 118 | self.ui.show() | ||
2644 | 119 | self.addCleanup(self.ui.hide) | ||
2645 | 120 | |||
2646 | 121 | self.assertFalse(self._called) | 111 | self.assertFalse(self._called) |
2647 | 112 | |||
2648 | 122 | self.ui.focus_changed(None, self.ui.ui.confirm_password_line_edit) | 113 | self.ui.focus_changed(None, self.ui.ui.confirm_password_line_edit) |
2649 | 114 | |||
2650 | 123 | self.assertTrue(self.ui.ui.password_assistance.isVisible()) | 115 | self.assertTrue(self.ui.ui.password_assistance.isVisible()) |
2651 | 124 | self.assertTrue(self._called) | 116 | self.assertTrue(self._called) |
2652 | 125 | 117 | ||
2653 | 126 | 118 | ||
2654 | === modified file 'ubuntu_sso/qt/tests/test_setup_account.py' | |||
2655 | --- ubuntu_sso/qt/tests/test_setup_account.py 2012-03-06 14:13:10 +0000 | |||
2656 | +++ ubuntu_sso/qt/tests/test_setup_account.py 2012-03-20 16:10:09 +0000 | |||
2657 | @@ -43,13 +43,20 @@ | |||
2658 | 43 | """ | 43 | """ |
2659 | 44 | self.ui.ui.name_edit.setText("") | 44 | self.ui.ui.name_edit.setText("") |
2660 | 45 | self.ui.name_assistance() | 45 | self.ui.name_assistance() |
2661 | 46 | self.ui.show() | ||
2662 | 47 | self.addCleanup(self.ui.hide) | ||
2663 | 48 | self.assertTrue(self.ui.ui.name_assistance.isVisible()) | 46 | self.assertTrue(self.ui.ui.name_assistance.isVisible()) |
2668 | 49 | self.assertEqual( | 47 | self.assert_error_correct(self.ui.ui.name_assistance, gui.EMPTY_NAME, |
2669 | 50 | unicode(self.ui.ui.name_assistance.text()), | 48 | max_width=self.ui.header.max_title_width) |
2670 | 51 | gui.ERROR_STYLE % gui.EMPTY_NAME) | 49 | |
2671 | 52 | self.ui.hide() | 50 | def test_hide_error_on_refresh_clicked(self): |
2672 | 51 | """Hide form errors when the user click to refresh the captcha.""" | ||
2673 | 52 | self.ui.show_error('error') | ||
2674 | 53 | self.assert_error_correct(self.ui.form_errors_label, 'error', | ||
2675 | 54 | max_width=self.ui.header.max_title_width) | ||
2676 | 55 | |||
2677 | 56 | self.ui.ui.refresh_label.linkActivated.emit('error') | ||
2678 | 57 | |||
2679 | 58 | message = unicode(self.ui.form_errors_label.text()) | ||
2680 | 59 | self.assertEqual(message, ' ') | ||
2681 | 53 | 60 | ||
2682 | 54 | def test_enable_setup_button_with_visible_check(self): | 61 | def test_enable_setup_button_with_visible_check(self): |
2683 | 55 | """Test _enable_setup_button method with terms check visible.""" | 62 | """Test _enable_setup_button method with terms check visible.""" |
2684 | @@ -65,8 +72,6 @@ | |||
2685 | 65 | self.ui.ui.captcha_solution_edit.setText('captcha solution') | 72 | self.ui.ui.captcha_solution_edit.setText('captcha solution') |
2686 | 66 | self.ui.terms_checkbox.setChecked(True) | 73 | self.ui.terms_checkbox.setChecked(True) |
2687 | 67 | 74 | ||
2688 | 68 | self.ui.show() | ||
2689 | 69 | self.addCleanup(self.ui.hide) | ||
2690 | 70 | self.ui.terms_checkbox.setVisible(True) | 75 | self.ui.terms_checkbox.setVisible(True) |
2691 | 71 | self.ui.ui.captcha_solution_edit.textEdited.emit('') | 76 | self.ui.ui.captcha_solution_edit.textEdited.emit('') |
2692 | 72 | self.assertTrue(self.ui.set_up_button.isEnabled()) | 77 | self.assertTrue(self.ui.set_up_button.isEnabled()) |
2693 | @@ -84,8 +89,6 @@ | |||
2694 | 84 | self.ui.ui.confirm_password_edit.setText(password) | 89 | self.ui.ui.confirm_password_edit.setText(password) |
2695 | 85 | self.ui.ui.captcha_solution_edit.setText('captcha solution') | 90 | self.ui.ui.captcha_solution_edit.setText('captcha solution') |
2696 | 86 | 91 | ||
2697 | 87 | self.ui.show() | ||
2698 | 88 | self.addCleanup(self.ui.hide) | ||
2699 | 89 | self.ui.terms_checkbox.setVisible(False) | 92 | self.ui.terms_checkbox.setVisible(False) |
2700 | 90 | self.ui.ui.captcha_solution_edit.textEdited.emit('') | 93 | self.ui.ui.captcha_solution_edit.textEdited.emit('') |
2701 | 91 | self.assertTrue(self.ui.set_up_button.isEnabled()) | 94 | self.assertTrue(self.ui.set_up_button.isEnabled()) |
2702 | @@ -114,8 +117,6 @@ | |||
2703 | 114 | 117 | ||
2704 | 115 | def test_password_focus_gain(self): | 118 | def test_password_focus_gain(self): |
2705 | 116 | """Check functions execution when focus_changed() is executed.""" | 119 | """Check functions execution when focus_changed() is executed.""" |
2706 | 117 | self.ui.show() | ||
2707 | 118 | self.addCleanup(self.ui.hide) | ||
2708 | 119 | self.ui.ui.password_assistance.setVisible(False) | 120 | self.ui.ui.password_assistance.setVisible(False) |
2709 | 120 | self.assertFalse(self.ui.ui.password_assistance.isVisible()) | 121 | self.assertFalse(self.ui.ui.password_assistance.isVisible()) |
2710 | 121 | self.patch(self.ui, 'name_assistance', self._set_called) | 122 | self.patch(self.ui, 'name_assistance', self._set_called) |
2711 | @@ -174,7 +175,6 @@ | |||
2712 | 174 | """Test on_user_registered method.""" | 175 | """Test on_user_registered method.""" |
2713 | 175 | email = 'email@example' | 176 | email = 'email@example' |
2714 | 176 | self.ui.ui.email_edit.setText(email) | 177 | self.ui.ui.email_edit.setText(email) |
2715 | 177 | |||
2716 | 178 | self.assert_signal_emitted(self.ui.userRegistered, (email,), | 178 | self.assert_signal_emitted(self.ui.userRegistered, (email,), |
2717 | 179 | self.ui.on_user_registered, self.app_name, email) | 179 | self.ui.on_user_registered, self.app_name, email) |
2718 | 180 | 180 | ||
2719 | @@ -193,8 +193,6 @@ | |||
2720 | 193 | def test_initialize_page(self): | 193 | def test_initialize_page(self): |
2721 | 194 | """Widgets are properly initialized.""" | 194 | """Widgets are properly initialized.""" |
2722 | 195 | self.ui.initializePage() | 195 | self.ui.initializePage() |
2723 | 196 | self.ui.show() | ||
2724 | 197 | self.addCleanup(self.ui.hide) | ||
2725 | 198 | 196 | ||
2726 | 199 | # set up account button | 197 | # set up account button |
2727 | 200 | expected = [QtGui.QWizard.BackButton, QtGui.QWizard.Stretch, | 198 | expected = [QtGui.QWizard.BackButton, QtGui.QWizard.Stretch, |
2728 | @@ -210,12 +208,15 @@ | |||
2729 | 210 | self.assertFalse(self.ui.captcha_received) | 208 | self.assertFalse(self.ui.captcha_received) |
2730 | 211 | 209 | ||
2731 | 212 | # labels | 210 | # labels |
2735 | 213 | self.assertEqual(self.ui.ui.name_label.text(), gui.NAME_ENTRY) | 211 | self.assertEqual(unicode(self.ui.ui.name_label.text()), |
2736 | 214 | self.assertEqual(self.ui.ui.email_label.text(), gui.EMAIL) | 212 | gui.NAME_ENTRY) |
2737 | 215 | self.assertEqual(self.ui.ui.confirm_email_label.text(), | 213 | self.assertEqual(unicode(self.ui.ui.email_label.text()), |
2738 | 214 | gui.EMAIL) | ||
2739 | 215 | self.assertEqual(unicode(self.ui.ui.confirm_email_label.text()), | ||
2740 | 216 | gui.RETYPE_EMAIL) | 216 | gui.RETYPE_EMAIL) |
2743 | 217 | self.assertEqual(self.ui.ui.password_label.text(), gui.PASSWORD) | 217 | self.assertEqual(unicode(self.ui.ui.password_label.text()), |
2744 | 218 | self.assertEqual(self.ui.ui.confirm_password_label.text(), | 218 | gui.PASSWORD) |
2745 | 219 | self.assertEqual(unicode(self.ui.ui.confirm_password_label.text()), | ||
2746 | 219 | gui.RETYPE_PASSWORD) | 220 | gui.RETYPE_PASSWORD) |
2747 | 220 | 221 | ||
2748 | 221 | # assistants | 222 | # assistants |
2749 | @@ -230,8 +231,6 @@ | |||
2750 | 230 | self.patch(self.ui, 'set_next_validation', self._set_called) | 231 | self.patch(self.ui, 'set_next_validation', self._set_called) |
2751 | 231 | self.ui.initializePage() | 232 | self.ui.initializePage() |
2752 | 232 | self.ui.captcha_received = True | 233 | self.ui.captcha_received = True |
2753 | 233 | self.ui.show() | ||
2754 | 234 | self.addCleanup(self.ui.hide) | ||
2755 | 235 | 234 | ||
2756 | 236 | self.ui.set_up_button.clicked.emit(False) | 235 | self.ui.set_up_button.clicked.emit(False) |
2757 | 237 | self.assertEqual(self._called, ((False,), {})) | 236 | self.assertEqual(self._called, ((False,), {})) |
2758 | @@ -239,13 +238,10 @@ | |||
2759 | 239 | def test_set_error_message(self): | 238 | def test_set_error_message(self): |
2760 | 240 | """Check the state of the label after calling: set_error_message.""" | 239 | """Check the state of the label after calling: set_error_message.""" |
2761 | 241 | self.ui.email_assistance() | 240 | self.ui.email_assistance() |
2762 | 242 | self.ui.show() | ||
2763 | 243 | self.addCleanup(self.ui.hide) | ||
2764 | 244 | self.ui.set_error_message(self.ui.ui.email_assistance, "message") | 241 | self.ui.set_error_message(self.ui.ui.email_assistance, "message") |
2765 | 245 | self.assertTrue(self.ui.ui.email_assistance.isVisible()) | 242 | self.assertTrue(self.ui.ui.email_assistance.isVisible()) |
2769 | 246 | self.assertEqual( | 243 | self.assert_error_correct(self.ui.ui.email_assistance, "message", |
2770 | 247 | unicode(self.ui.ui.email_assistance.text()), | 244 | max_width=self.ui.header.max_title_width) |
2768 | 248 | gui.ERROR_STYLE % "message") | ||
2771 | 249 | 245 | ||
2772 | 250 | def test_blank_name(self): | 246 | def test_blank_name(self): |
2773 | 251 | """Status when the name field is blank (spaces). | 247 | """Status when the name field is blank (spaces). |
2774 | @@ -255,13 +251,9 @@ | |||
2775 | 255 | """ | 251 | """ |
2776 | 256 | self.ui.ui.name_edit.setText(" ") | 252 | self.ui.ui.name_edit.setText(" ") |
2777 | 257 | self.ui.name_assistance() | 253 | self.ui.name_assistance() |
2778 | 258 | self.ui.show() | ||
2779 | 259 | self.addCleanup(self.ui.hide) | ||
2780 | 260 | self.assertTrue(self.ui.ui.name_assistance.isVisible()) | 254 | self.assertTrue(self.ui.ui.name_assistance.isVisible()) |
2785 | 261 | self.assertEqual( | 255 | self.assert_error_correct(self.ui.ui.name_assistance, gui.EMPTY_NAME, |
2786 | 262 | unicode(self.ui.ui.name_assistance.text()), | 256 | max_width=self.ui.header.max_title_width) |
2783 | 263 | gui.ERROR_STYLE % gui.EMPTY_NAME) | ||
2784 | 264 | self.ui.hide() | ||
2787 | 265 | 257 | ||
2788 | 266 | def test_valid_name(self): | 258 | def test_valid_name(self): |
2789 | 267 | """Status when the name field is valid. | 259 | """Status when the name field is valid. |
2790 | @@ -270,10 +262,7 @@ | |||
2791 | 270 | """ | 262 | """ |
2792 | 271 | self.ui.ui.name_edit.setText("John Doe") | 263 | self.ui.ui.name_edit.setText("John Doe") |
2793 | 272 | self.ui.name_assistance() | 264 | self.ui.name_assistance() |
2794 | 273 | self.ui.show() | ||
2795 | 274 | self.addCleanup(self.ui.hide) | ||
2796 | 275 | self.assertFalse(self.ui.ui.name_assistance.isVisible()) | 265 | self.assertFalse(self.ui.ui.name_assistance.isVisible()) |
2797 | 276 | self.ui.hide() | ||
2798 | 277 | 266 | ||
2799 | 278 | def test_invalid_email(self): | 267 | def test_invalid_email(self): |
2800 | 279 | """Status when the email field has no @. | 268 | """Status when the email field has no @. |
2801 | @@ -283,12 +272,10 @@ | |||
2802 | 283 | """ | 272 | """ |
2803 | 284 | self.ui.ui.email_edit.setText("foobar") | 273 | self.ui.ui.email_edit.setText("foobar") |
2804 | 285 | self.ui.email_assistance() | 274 | self.ui.email_assistance() |
2805 | 286 | self.ui.show() | ||
2806 | 287 | self.addCleanup(self.ui.hide) | ||
2807 | 288 | self.assertTrue(self.ui.ui.email_assistance.isVisible()) | 275 | self.assertTrue(self.ui.ui.email_assistance.isVisible()) |
2811 | 289 | self.assertEqual( | 276 | self.assert_error_correct(self.ui.ui.email_assistance, |
2812 | 290 | unicode(self.ui.ui.email_assistance.text()), | 277 | gui.INVALID_EMAIL, |
2813 | 291 | gui.ERROR_STYLE % gui.INVALID_EMAIL) | 278 | max_width=self.ui.header.max_title_width) |
2814 | 292 | 279 | ||
2815 | 293 | def test_valid_email(self): | 280 | def test_valid_email(self): |
2816 | 294 | """Status when the email field has a @. | 281 | """Status when the email field has a @. |
2817 | @@ -297,10 +284,7 @@ | |||
2818 | 297 | """ | 284 | """ |
2819 | 298 | self.ui.ui.email_edit.setText("foo@bar") | 285 | self.ui.ui.email_edit.setText("foo@bar") |
2820 | 299 | self.ui.email_assistance() | 286 | self.ui.email_assistance() |
2821 | 300 | self.ui.show() | ||
2822 | 301 | self.addCleanup(self.ui.hide) | ||
2823 | 302 | self.assertFalse(self.ui.ui.email_assistance.isVisible()) | 287 | self.assertFalse(self.ui.ui.email_assistance.isVisible()) |
2824 | 303 | self.ui.hide() | ||
2825 | 304 | 288 | ||
2826 | 305 | def test_matching_emails(self): | 289 | def test_matching_emails(self): |
2827 | 306 | """Status when the email fields match. | 290 | """Status when the email fields match. |
2828 | @@ -310,10 +294,7 @@ | |||
2829 | 310 | self.ui.ui.email_edit.setText("foo@bar") | 294 | self.ui.ui.email_edit.setText("foo@bar") |
2830 | 311 | self.ui.ui.confirm_email_edit.setText("foo@bar") | 295 | self.ui.ui.confirm_email_edit.setText("foo@bar") |
2831 | 312 | self.ui.confirm_email_assistance() | 296 | self.ui.confirm_email_assistance() |
2832 | 313 | self.ui.show() | ||
2833 | 314 | self.addCleanup(self.ui.hide) | ||
2834 | 315 | self.assertFalse(self.ui.ui.confirm_email_assistance.isVisible()) | 297 | self.assertFalse(self.ui.ui.confirm_email_assistance.isVisible()) |
2835 | 316 | self.ui.hide() | ||
2836 | 317 | 298 | ||
2837 | 318 | def test_not_matching_emails(self): | 299 | def test_not_matching_emails(self): |
2838 | 319 | """Status when the email fields don't match. | 300 | """Status when the email fields don't match. |
2839 | @@ -324,18 +305,13 @@ | |||
2840 | 324 | self.ui.ui.email_edit.setText("foo@bar") | 305 | self.ui.ui.email_edit.setText("foo@bar") |
2841 | 325 | self.ui.ui.confirm_email_edit.setText("foo@baz") | 306 | self.ui.ui.confirm_email_edit.setText("foo@baz") |
2842 | 326 | self.ui.confirm_email_assistance() | 307 | self.ui.confirm_email_assistance() |
2843 | 327 | self.ui.show() | ||
2844 | 328 | self.addCleanup(self.ui.hide) | ||
2845 | 329 | self.assertTrue(self.ui.ui.confirm_email_assistance.isVisible()) | 308 | self.assertTrue(self.ui.ui.confirm_email_assistance.isVisible()) |
2850 | 330 | self.assertEqual( | 309 | self.assert_error_correct(self.ui.ui.confirm_email_assistance, |
2851 | 331 | unicode(self.ui.ui.confirm_email_assistance.text()), | 310 | gui.EMAIL_MATCH, |
2852 | 332 | gui.ERROR_STYLE % gui.EMAIL_MATCH) | 311 | max_width=self.ui.header.max_title_width) |
2849 | 333 | self.ui.hide() | ||
2853 | 334 | 312 | ||
2854 | 335 | def test_focus_changed_password_visibility(self): | 313 | def test_focus_changed_password_visibility(self): |
2855 | 336 | """Check visibility changes when focus_changed() is executed.""" | 314 | """Check visibility changes when focus_changed() is executed.""" |
2856 | 337 | self.ui.show() | ||
2857 | 338 | self.addCleanup(self.ui.hide) | ||
2858 | 339 | self.ui.focus_changed(None, self.ui.ui.password_edit) | 315 | self.ui.focus_changed(None, self.ui.ui.password_edit) |
2859 | 340 | self.assertTrue(self.ui.ui.password_assistance.isVisible()) | 316 | self.assertTrue(self.ui.ui.password_assistance.isVisible()) |
2860 | 341 | 317 | ||
2861 | @@ -350,33 +326,41 @@ | |||
2862 | 350 | self.ui.showEvent(QtGui.QShowEvent()) | 326 | self.ui.showEvent(QtGui.QShowEvent()) |
2863 | 351 | self.ui.hideEvent(QtGui.QHideEvent()) | 327 | self.ui.hideEvent(QtGui.QHideEvent()) |
2864 | 352 | 328 | ||
2866 | 353 | def test_on_captcha_refreshing(self): | 329 | def test_on_captcha_refreshing_visible(self): |
2867 | 354 | """Check the state of the overlay on captcha refreshing.""" | 330 | """Check the state of the overlay on captcha refreshing.""" |
2878 | 355 | self.assertEqual(self._overlay_show_counter, 0) | 331 | self.ui.hide_overlay() |
2879 | 356 | self.ui.on_captcha_refreshing() | 332 | |
2880 | 357 | self.assertEqual(self._overlay_show_counter, 0) | 333 | self.assertEqual(self._overlay_show_counter, 0) |
2881 | 358 | self.assertTrue(self.ui.isEnabled()) | 334 | self.assertTrue(self.ui.isEnabled()) |
2882 | 359 | self.ui.captcha_received = True | 335 | |
2883 | 360 | self.ui.show() | 336 | self.ui.on_captcha_refreshing() |
2884 | 361 | self.addCleanup(self.ui.hide) | 337 | |
2875 | 362 | self.assertEqual(self._overlay_show_counter, 0) | ||
2876 | 363 | self.assertTrue(self.ui.isEnabled()) | ||
2877 | 364 | self.ui.on_captcha_refreshing() | ||
2885 | 365 | self.assertFalse(self.ui.isEnabled()) | 338 | self.assertFalse(self.ui.isEnabled()) |
2886 | 366 | self.assertEqual(self._overlay_show_counter, 1) | 339 | self.assertEqual(self._overlay_show_counter, 1) |
2887 | 367 | 340 | ||
2888 | 341 | def test_on_captcha_refreshing_not_visible(self): | ||
2889 | 342 | """Check the state of the overlay on captcha refreshing.""" | ||
2890 | 343 | self.ui.hide_overlay() | ||
2891 | 344 | |||
2892 | 345 | self.assertEqual(self._overlay_show_counter, 0) | ||
2893 | 346 | self.assertTrue(self.ui.isEnabled()) | ||
2894 | 347 | |||
2895 | 348 | self.ui.hide() | ||
2896 | 349 | self.ui.on_captcha_refreshing() | ||
2897 | 350 | |||
2898 | 351 | self.assertEqual(self._overlay_show_counter, 0) | ||
2899 | 352 | self.assertTrue(self.ui.isEnabled()) | ||
2900 | 353 | |||
2901 | 368 | def test_on_captcha_refresh_complete(self): | 354 | def test_on_captcha_refresh_complete(self): |
2902 | 369 | """Check the state of the overlay on captcha refreshing complete.""" | 355 | """Check the state of the overlay on captcha refreshing complete.""" |
2903 | 370 | self.assertEqual(self._overlay_hide_counter, 0) | 356 | self.assertEqual(self._overlay_hide_counter, 0) |
2905 | 371 | self.assertTrue(self.ui.isEnabled()) | 357 | |
2906 | 372 | self.ui.on_captcha_refresh_complete() | 358 | self.ui.on_captcha_refresh_complete() |
2907 | 359 | |||
2908 | 373 | self.assertEqual(self._overlay_hide_counter, 1) | 360 | self.assertEqual(self._overlay_hide_counter, 1) |
2909 | 374 | self.assertTrue(self.ui.isEnabled()) | ||
2910 | 375 | 361 | ||
2911 | 376 | def test_hide_error_on_refresh_captcha(self): | 362 | def test_hide_error_on_refresh_captcha(self): |
2912 | 377 | """Test that the errors are hidden on refresh captcha.""" | 363 | """Test that the errors are hidden on refresh captcha.""" |
2916 | 378 | self.ui.show() | 364 | self.ui.show_error('error-message') |
2914 | 379 | self.addCleanup(self.ui.hide) | ||
2915 | 380 | self.ui.show_error(self.app_name, 'error-message') | ||
2917 | 381 | self.ui.ui.refresh_label.linkActivated.emit('link') | 365 | self.ui.ui.refresh_label.linkActivated.emit('link') |
2918 | 382 | self.assertEqual(self.ui.form_errors_label.text(), ' ') | 366 | self.assertEqual(self.ui.form_errors_label.text(), ' ') |
2919 | 383 | 367 | ||
2920 | === modified file 'ubuntu_sso/qt/tests/test_ssl_dialog.py' | |||
2921 | --- ubuntu_sso/qt/tests/test_ssl_dialog.py 2012-03-05 16:32:37 +0000 | |||
2922 | +++ ubuntu_sso/qt/tests/test_ssl_dialog.py 2012-03-20 16:10:09 +0000 | |||
2923 | @@ -142,7 +142,8 @@ | |||
2924 | 142 | 142 | ||
2925 | 143 | def test_set_expander(self): | 143 | def test_set_expander(self): |
2926 | 144 | """Test that the expander is correctly set.""" | 144 | """Test that the expander is correctly set.""" |
2928 | 145 | self.assertEqual(SSL_CERT_DETAILS, self.dialog.expander.text()) | 145 | self.assertEqual(SSL_CERT_DETAILS, |
2929 | 146 | unicode(self.dialog.expander.text())) | ||
2930 | 146 | self.assertNotEqual(None, self.dialog.expander.content) | 147 | self.assertNotEqual(None, self.dialog.expander.content) |
2931 | 147 | self.assertEqual(2, self.dialog.ui.expander_layout.indexOf( | 148 | self.assertEqual(2, self.dialog.ui.expander_layout.indexOf( |
2932 | 148 | self.dialog.expander)) | 149 | self.dialog.expander)) |
2933 | 149 | 150 | ||
2934 | === modified file 'ubuntu_sso/qt/tests/test_sso_wizard_page.py' | |||
2935 | --- ubuntu_sso/qt/tests/test_sso_wizard_page.py 2012-03-05 18:56:50 +0000 | |||
2936 | +++ ubuntu_sso/qt/tests/test_sso_wizard_page.py 2012-03-20 16:10:09 +0000 | |||
2937 | @@ -16,98 +16,137 @@ | |||
2938 | 16 | 16 | ||
2939 | 17 | """Test the SSOWizardPage and related.""" | 17 | """Test the SSOWizardPage and related.""" |
2940 | 18 | 18 | ||
2950 | 19 | from twisted.internet import defer | 19 | from ubuntu_sso.qt import PREFERED_UI_SIZE, sso_wizard_page as gui |
2951 | 20 | 20 | from ubuntu_sso.qt.tests import ( | |
2952 | 21 | from ubuntu_sso.qt import PREFERED_UI_SIZE | 21 | APP_NAME, |
2953 | 22 | from ubuntu_sso.qt.setup_account_page import SetupAccountPage | 22 | BaseTestCase, |
2954 | 23 | from ubuntu_sso.qt.sso_wizard_page import Header | 23 | build_string_for_pixels, |
2955 | 24 | from ubuntu_sso.qt.tests import BaseTestCase, PageBaseTestCase | 24 | PageBaseTestCase, |
2956 | 25 | 25 | ) | |
2957 | 26 | 26 | ||
2958 | 27 | class HeaderTest(BaseTestCase): | 27 | |
2959 | 28 | MAX_WIDTH = 100 | ||
2960 | 29 | |||
2961 | 30 | |||
2962 | 31 | class WizardHeaderTestCase(BaseTestCase): | ||
2963 | 28 | 32 | ||
2964 | 29 | """Tests for injected Header in each Wizard Page.""" | 33 | """Tests for injected Header in each Wizard Page.""" |
2965 | 30 | 34 | ||
2970 | 31 | @defer.inlineCallbacks | 35 | kwargs = dict(max_width=MAX_WIDTH) |
2971 | 32 | def setUp(self): | 36 | ui_class = gui.WizardHeader |
2972 | 33 | yield super(HeaderTest, self).setUp() | 37 | ui_wizard_class = None |
2969 | 34 | self.header = Header() | ||
2973 | 35 | 38 | ||
2974 | 36 | def test_label_state(self): | 39 | def test_label_state(self): |
2975 | 37 | """Check the title and subtitle properties.""" | 40 | """Check the title and subtitle properties.""" |
2980 | 38 | self.assertTrue(self.header.title_label.wordWrap()) | 41 | self.assertTrue(self.ui.title_label.wordWrap()) |
2981 | 39 | self.assertTrue(self.header.subtitle_label.wordWrap()) | 42 | self.assertTrue(self.ui.subtitle_label.wordWrap()) |
2982 | 40 | self.assertFalse(self.header.title_label.isVisible()) | 43 | self.assertFalse(self.ui.title_label.isVisible()) |
2983 | 41 | self.assertFalse(self.header.subtitle_label.isVisible()) | 44 | self.assertFalse(self.ui.subtitle_label.isVisible()) |
2984 | 42 | 45 | ||
2985 | 43 | def test_set_title(self): | 46 | def test_set_title(self): |
2986 | 44 | """Check if set_title works ok, showing the widget if necessary.""" | 47 | """Check if set_title works ok, showing the widget if necessary.""" |
2992 | 45 | self.header.set_title('title') | 48 | max_width = self.ui.max_title_width |
2993 | 46 | self.assertEqual(self.header.title_label.text(), 'title') | 49 | text = build_string_for_pixels(self.ui.title_label, max_width) |
2994 | 47 | self.header.show() | 50 | |
2995 | 48 | self.assertTrue(self.header.title_label.isVisible()) | 51 | self.ui.set_title(text) |
2996 | 49 | self.header.hide() | 52 | |
2997 | 53 | self.assert_title_correct(self.ui.title_label, text, max_width) | ||
2998 | 50 | 54 | ||
2999 | 51 | def test_set_elided_title(self): | 55 | def test_set_elided_title(self): |
3000 | 52 | """Check if set_title adds the ellipsis when necessary.""" | 56 | """Check if set_title adds the ellipsis when necessary.""" |
3001 | 53 | # add an extra letter so we ensure this needs to be trimmed | 57 | # add an extra letter so we ensure this needs to be trimmed |
3007 | 54 | title = 'a' * int(PREFERED_UI_SIZE['width'] * 0.95) + 'a' | 58 | max_width = self.ui.max_title_width |
3008 | 55 | self.header.set_title(title) | 59 | text = build_string_for_pixels(self.ui.title_label, max_width + 10) |
3009 | 56 | self.assertEqual(self.header.title_label.toolTip(), title) | 60 | |
3010 | 57 | expected = unicode(self.header.title_label.text()) | 61 | self.ui.set_title(text) |
3011 | 58 | self.assertTrue(expected.endswith(u'\u2026')) | 62 | |
3012 | 63 | self.assert_title_correct(self.ui.title_label, text, max_width) | ||
3013 | 59 | 64 | ||
3014 | 60 | def test_set_empty_title(self): | 65 | def test_set_empty_title(self): |
3015 | 61 | """Check if the widget is hidden for empty title.""" | 66 | """Check if the widget is hidden for empty title.""" |
3018 | 62 | self.header.set_title('') | 67 | self.ui.set_title('') |
3019 | 63 | self.assertFalse(self.header.title_label.isVisible()) | 68 | |
3020 | 69 | self.assertEqual(self.ui.title_label.toolTip(), '') | ||
3021 | 70 | self.assertFalse(self.ui.title_label.isVisible()) | ||
3022 | 64 | 71 | ||
3023 | 65 | def test_set_subtitle(self): | 72 | def test_set_subtitle(self): |
3024 | 66 | """Check if set_subtitle works ok, showing the widget if necessary.""" | 73 | """Check if set_subtitle works ok, showing the widget if necessary.""" |
3030 | 67 | self.header.set_subtitle('subtitle') | 74 | max_width = self.ui.max_subtitle_width |
3031 | 68 | self.assertEqual(self.header.subtitle_label.text(), 'subtitle') | 75 | text = build_string_for_pixels(self.ui.subtitle_label, max_width) |
3032 | 69 | self.header.show() | 76 | |
3033 | 70 | self.assertTrue(self.header.subtitle_label.isVisible()) | 77 | self.ui.set_subtitle(text) |
3034 | 71 | self.header.hide() | 78 | |
3035 | 79 | self.assert_subtitle_correct(self.ui.subtitle_label, text, max_width) | ||
3036 | 72 | 80 | ||
3037 | 73 | def test_set_elided_subtitle(self): | 81 | def test_set_elided_subtitle(self): |
3038 | 74 | """Check if set_subtitle adds the ellipsis when necessary.""" | 82 | """Check if set_subtitle adds the ellipsis when necessary.""" |
3044 | 75 | subtitle = 'a' * int(PREFERED_UI_SIZE['width'] * 0.95) + 'a' | 83 | max_width = self.ui.max_subtitle_width |
3045 | 76 | self.header.set_subtitle(subtitle) | 84 | text = build_string_for_pixels(self.ui.subtitle_label, max_width + 10) |
3046 | 77 | self.assertEqual(self.header.subtitle_label.toolTip(), subtitle) | 85 | |
3047 | 78 | expected = unicode(self.header.subtitle_label.text()) | 86 | self.ui.set_subtitle(text) |
3048 | 79 | self.assertTrue(expected.endswith(u'\u2026')) | 87 | |
3049 | 88 | self.assert_subtitle_correct(self.ui.subtitle_label, text, max_width) | ||
3050 | 80 | 89 | ||
3051 | 81 | def test_set_empty_subtitle(self): | 90 | def test_set_empty_subtitle(self): |
3052 | 82 | """Check if the widget is hidden for empty subtitle.""" | 91 | """Check if the widget is hidden for empty subtitle.""" |
3062 | 83 | self.header.set_title('') | 92 | self.ui.set_subtitle('') |
3063 | 84 | self.assertFalse(self.header.title_label.isVisible()) | 93 | |
3064 | 85 | 94 | self.assertEqual(self.ui.subtitle_label.toolTip(), '') | |
3065 | 86 | 95 | self.assertFalse(self.ui.subtitle_label.isVisible()) | |
3066 | 87 | class SSOWizardPageTest(PageBaseTestCase): | 96 | |
3067 | 88 | 97 | ||
3068 | 89 | """Tests for SSOWizardPage.""" | 98 | class BaseWizardPageTestCase(PageBaseTestCase): |
3069 | 90 | 99 | ||
3070 | 91 | ui_class = SetupAccountPage | 100 | """Tests for SSOWizardPage.""" |
3071 | 101 | |||
3072 | 102 | kwargs = {} | ||
3073 | 103 | ui_class = gui.BaseWizardPage | ||
3074 | 104 | |||
3075 | 105 | def test_max_width(self): | ||
3076 | 106 | """The max_width is correct.""" | ||
3077 | 107 | self.assertEqual(self.ui.max_width, 0) | ||
3078 | 108 | |||
3079 | 109 | |||
3080 | 110 | class SSOWizardPageTestCase(BaseWizardPageTestCase): | ||
3081 | 111 | |||
3082 | 112 | """Tests for SSOWizardPage.""" | ||
3083 | 113 | |||
3084 | 114 | kwargs = dict(app_name=APP_NAME) | ||
3085 | 115 | ui_class = gui.SSOWizardPage | ||
3086 | 116 | |||
3087 | 117 | def test_max_width(self): | ||
3088 | 118 | """The max_width is correct.""" | ||
3089 | 119 | self.assertEqual(self.ui.max_width, PREFERED_UI_SIZE['width']) | ||
3090 | 92 | 120 | ||
3091 | 93 | def test_show_error(self): | 121 | def test_show_error(self): |
3092 | 94 | """Test show_error with a normal length string.""" | 122 | """Test show_error with a normal length string.""" |
3093 | 95 | message = 'error-message' | 123 | message = 'error-message' |
3098 | 96 | self.ui.show_error(self.app_name, message) | 124 | self.ui.show_error(message) |
3099 | 97 | self.assertEqual(self.ui.form_errors_label.toolTip(), message) | 125 | |
3100 | 98 | expected = unicode(self.ui.form_errors_label.text()) | 126 | self.assert_error_correct(self.ui.form_errors_label, message, |
3101 | 99 | self.assertEqual(expected, message) | 127 | self.ui.header.max_title_width) |
3102 | 100 | 128 | ||
3103 | 101 | def test_show_error_long_text(self): | 129 | def test_show_error_long_text(self): |
3104 | 102 | """Test show_error with a long length string.""" | 130 | """Test show_error with a long length string.""" |
3110 | 103 | message = 'a' * int(PREFERED_UI_SIZE['width'] * 0.95) + 'a' | 131 | message = build_string_for_pixels(self.ui.form_errors_label, |
3111 | 104 | self.ui.show_error(self.app_name, message) | 132 | self.ui.header.max_title_width + 10) |
3112 | 105 | self.assertEqual(self.ui.form_errors_label.toolTip(), message) | 133 | |
3113 | 106 | expected = unicode(self.ui.form_errors_label.text()) | 134 | self.ui.show_error(message) |
3114 | 107 | self.assertTrue(expected.endswith(u'\u2026')) | 135 | self.assert_error_correct(self.ui.form_errors_label, message, |
3115 | 136 | self.ui.header.max_title_width) | ||
3116 | 108 | 137 | ||
3117 | 109 | def test_hide_error(self): | 138 | def test_hide_error(self): |
3118 | 110 | """Test show_error with a long length string.""" | 139 | """Test show_error with a long length string.""" |
3119 | 111 | message = ' ' | ||
3120 | 112 | self.ui.hide_error() | 140 | self.ui.hide_error() |
3122 | 113 | self.assertEqual(self.ui.form_errors_label.text(), message) | 141 | |
3123 | 142 | self.assertEqual(self.ui.form_errors_label.text(), ' ') | ||
3124 | 143 | |||
3125 | 144 | def test_setup_page_with_failing_backend(self): | ||
3126 | 145 | """Test how the ui react with an invalid backend.""" | ||
3127 | 146 | self.patch(gui.main, "get_sso_client", lambda: None) | ||
3128 | 147 | self.patch(self.ui, "show_error", self._set_called) | ||
3129 | 148 | self.ui.setup_page() | ||
3130 | 149 | reason = 'There was a problem trying to setup the page %r' % self.ui | ||
3131 | 150 | expected = ((reason,), {}) | ||
3132 | 151 | self.assertEqual(expected, self._called) | ||
3133 | 152 | self.assertFalse(self.ui.isEnabled()) | ||
3134 | 114 | 153 | ||
3135 | === modified file 'ubuntu_sso/qt/tests/test_ubuntu_sso_wizard.py' | |||
3136 | --- ubuntu_sso/qt/tests/test_ubuntu_sso_wizard.py 2012-03-05 14:37:43 +0000 | |||
3137 | +++ ubuntu_sso/qt/tests/test_ubuntu_sso_wizard.py 2012-03-20 16:10:09 +0000 | |||
3138 | @@ -19,6 +19,11 @@ | |||
3139 | 19 | from PyQt4 import QtGui | 19 | from PyQt4 import QtGui |
3140 | 20 | from twisted.internet import defer | 20 | from twisted.internet import defer |
3141 | 21 | 21 | ||
3142 | 22 | from ubuntu_sso.tests import ( | ||
3143 | 23 | APP_NAME, | ||
3144 | 24 | EMAIL, | ||
3145 | 25 | PASSWORD, | ||
3146 | 26 | ) | ||
3147 | 22 | from ubuntu_sso.qt import PREFERED_UI_SIZE, ubuntu_sso_wizard | 27 | from ubuntu_sso.qt import PREFERED_UI_SIZE, ubuntu_sso_wizard |
3148 | 23 | from ubuntu_sso.qt.tests import ( | 28 | from ubuntu_sso.qt.tests import ( |
3149 | 24 | BaseTestCase, | 29 | BaseTestCase, |
3150 | @@ -86,6 +91,11 @@ | |||
3151 | 86 | finish_button.clicked.emit(True) | 91 | finish_button.clicked.emit(True) |
3152 | 87 | self.assertEqual(self._called, ((None,), {})) | 92 | self.assertEqual(self._called, ((None,), {})) |
3153 | 88 | 93 | ||
3154 | 94 | def test_window_title(self): | ||
3155 | 95 | """Check the window title for the application.""" | ||
3156 | 96 | title = unicode(self.ui.windowTitle()) | ||
3157 | 97 | self.assertEqual(title, ubuntu_sso_wizard.WINDOW_TITLE) | ||
3158 | 98 | |||
3159 | 89 | 99 | ||
3160 | 90 | class UbuntuSSOWizardTestCase(BaseTestCase): | 100 | class UbuntuSSOWizardTestCase(BaseTestCase): |
3161 | 91 | 101 | ||
3162 | @@ -102,13 +112,9 @@ | |||
3163 | 102 | 112 | ||
3164 | 103 | def test_window_size(self): | 113 | def test_window_size(self): |
3165 | 104 | """check the window size.""" | 114 | """check the window size.""" |
3166 | 105 | self.ui.show() | ||
3167 | 106 | self.addCleanup(self.ui.hide) | ||
3168 | 107 | size = self.ui.size() | 115 | size = self.ui.size() |
3173 | 108 | # pylint: disable=E1101 | 116 | self.assertTrue(size.height() >= PREFERED_UI_SIZE['height']) |
3174 | 109 | self.assertGreaterEqual(size.height(), PREFERED_UI_SIZE['height']) | 117 | self.assertTrue(size.width() >= PREFERED_UI_SIZE['width']) |
3171 | 110 | self.assertGreaterEqual(size.width(), PREFERED_UI_SIZE['width']) | ||
3172 | 111 | # pylint: enable=E1101 | ||
3175 | 112 | 118 | ||
3176 | 113 | def test_move_to_success_page_state(self): | 119 | def test_move_to_success_page_state(self): |
3177 | 114 | """Test _move_to_success_page method.""" | 120 | """Test _move_to_success_page method.""" |
3178 | @@ -123,6 +129,9 @@ | |||
3179 | 123 | 129 | ||
3180 | 124 | def test_overlay_shows(self): | 130 | def test_overlay_shows(self): |
3181 | 125 | """Test if the signals call the overlay.show properly.""" | 131 | """Test if the signals call the overlay.show properly.""" |
3182 | 132 | # reset the counter | ||
3183 | 133 | self.ui.overlay.show_counter = 0 | ||
3184 | 134 | |||
3185 | 126 | for page in self.ui._pages: | 135 | for page in self.ui._pages: |
3186 | 127 | page.show_overlay() | 136 | page.show_overlay() |
3187 | 128 | 137 | ||
3188 | @@ -130,6 +139,9 @@ | |||
3189 | 130 | 139 | ||
3190 | 131 | def test_overlay_hides(self): | 140 | def test_overlay_hides(self): |
3191 | 132 | """Test if the signals call the overlay.show properly.""" | 141 | """Test if the signals call the overlay.show properly.""" |
3192 | 142 | # reset the counter | ||
3193 | 143 | self.ui.overlay.show_counter = 0 | ||
3194 | 144 | |||
3195 | 133 | for page in self.ui._pages: | 145 | for page in self.ui._pages: |
3196 | 134 | page.hide_overlay() | 146 | page.hide_overlay() |
3197 | 135 | 147 | ||
3198 | @@ -146,3 +158,21 @@ | |||
3199 | 146 | self.ui.reset_password.passwordChanged.emit('') | 158 | self.ui.reset_password.passwordChanged.emit('') |
3200 | 147 | expected = ((self.ui.current_user,), {}) | 159 | expected = ((self.ui.current_user,), {}) |
3201 | 148 | self.assertEqual(expected, self._called) | 160 | self.assertEqual(expected, self._called) |
3202 | 161 | |||
3203 | 162 | def test_email_verification_page_params_from_current_user(self): | ||
3204 | 163 | """Tests that email_verification_page receives the proper params.""" | ||
3205 | 164 | self.ui._next_id = self.ui.current_user_page_id | ||
3206 | 165 | self.ui.next() | ||
3207 | 166 | self.ui.current_user.ui.email_edit.setText(EMAIL) | ||
3208 | 167 | self.ui.current_user.ui.password_edit.setText(PASSWORD) | ||
3209 | 168 | self.ui.current_user.on_user_not_validated(APP_NAME, EMAIL) | ||
3210 | 169 | self.assertEqual(EMAIL, self.ui.email_verification.email) | ||
3211 | 170 | self.assertEqual(PASSWORD, self.ui.email_verification.password) | ||
3212 | 171 | |||
3213 | 172 | def test_email_verification_page_params_from_setup(self): | ||
3214 | 173 | """Tests that email_verification_page receives the proper params.""" | ||
3215 | 174 | self.ui.setup_account.ui.email_edit.setText(EMAIL) | ||
3216 | 175 | self.ui.setup_account.ui.password_edit.setText(PASSWORD) | ||
3217 | 176 | self.ui.setup_account.on_user_registered(APP_NAME, {}) | ||
3218 | 177 | self.assertEqual(EMAIL, self.ui.email_verification.email) | ||
3219 | 178 | self.assertEqual(PASSWORD, self.ui.email_verification.password) | ||
3220 | 149 | 179 | ||
3221 | === modified file 'ubuntu_sso/qt/ubuntu_sso_wizard.py' | |||
3222 | --- ubuntu_sso/qt/ubuntu_sso_wizard.py 2012-03-05 20:30:57 +0000 | |||
3223 | +++ ubuntu_sso/qt/ubuntu_sso_wizard.py 2012-03-20 16:10:09 +0000 | |||
3224 | @@ -30,7 +30,7 @@ | |||
3225 | 30 | USER_SUCCESS, | 30 | USER_SUCCESS, |
3226 | 31 | ) | 31 | ) |
3227 | 32 | from ubuntu_sso.logger import setup_gui_logging | 32 | from ubuntu_sso.logger import setup_gui_logging |
3229 | 33 | from ubuntu_sso.qt import PREFERED_UI_SIZE | 33 | from ubuntu_sso.qt import PREFERED_UI_SIZE, WINDOW_TITLE |
3230 | 34 | from ubuntu_sso.qt.current_user_sign_in_page import CurrentUserSignInPage | 34 | from ubuntu_sso.qt.current_user_sign_in_page import CurrentUserSignInPage |
3231 | 35 | from ubuntu_sso.qt.email_verification_page import EmailVerificationPage | 35 | from ubuntu_sso.qt.email_verification_page import EmailVerificationPage |
3232 | 36 | from ubuntu_sso.qt.error_page import ErrorPage | 36 | from ubuntu_sso.qt.error_page import ErrorPage |
3233 | @@ -141,6 +141,8 @@ | |||
3234 | 141 | 141 | ||
3235 | 142 | def _go_back_to_page(self, page): | 142 | def _go_back_to_page(self, page): |
3236 | 143 | """Move back until it reaches the 'page'.""" | 143 | """Move back until it reaches the 'page'.""" |
3237 | 144 | logger.debug('Moving back from page: %s, to page: %s', | ||
3238 | 145 | self.currentPage(), page) | ||
3239 | 144 | page_id = self._pages[page] | 146 | page_id = self._pages[page] |
3240 | 145 | visited_pages = self.visitedPages() | 147 | visited_pages = self.visitedPages() |
3241 | 146 | for index in reversed(visited_pages): | 148 | for index in reversed(visited_pages): |
3242 | @@ -150,30 +152,42 @@ | |||
3243 | 150 | 152 | ||
3244 | 151 | def _move_to_reset_password_page(self): | 153 | def _move_to_reset_password_page(self): |
3245 | 152 | """Move to the reset password page wizard.""" | 154 | """Move to the reset password page wizard.""" |
3246 | 155 | logger.debug('Moving to ResetPasswordPage from: %s', | ||
3247 | 156 | self.currentPage()) | ||
3248 | 153 | self._next_id = self.reset_password_page_id | 157 | self._next_id = self.reset_password_page_id |
3249 | 154 | self.next() | 158 | self.next() |
3250 | 155 | self._next_id = -1 | 159 | self._next_id = -1 |
3251 | 156 | 160 | ||
3253 | 157 | def _move_to_email_verification_page(self): | 161 | def _move_to_email_verification_page(self, email): |
3254 | 158 | """Move to the email verification page wizard.""" | 162 | """Move to the email verification page wizard.""" |
3255 | 163 | logger.debug('Moving to EmailVerificationPage from: %s', | ||
3256 | 164 | self.currentPage()) | ||
3257 | 159 | self._next_id = self.email_verification_page_id | 165 | self._next_id = self.email_verification_page_id |
3258 | 166 | self.email_verification.email = unicode(email) | ||
3259 | 167 | self.email_verification.password = self.currentPage().password | ||
3260 | 160 | self.next() | 168 | self.next() |
3261 | 161 | self._next_id = -1 | 169 | self._next_id = -1 |
3262 | 162 | 170 | ||
3263 | 163 | def _move_to_setup_account_page(self): | 171 | def _move_to_setup_account_page(self): |
3264 | 164 | """Move to the setup account page wizard.""" | 172 | """Move to the setup account page wizard.""" |
3265 | 173 | logger.debug('Moving to SetupAccountPage from: %s', | ||
3266 | 174 | self.currentPage()) | ||
3267 | 165 | self._next_id = self.setup_account_page_id | 175 | self._next_id = self.setup_account_page_id |
3268 | 166 | self.next() | 176 | self.next() |
3269 | 167 | self._next_id = -1 | 177 | self._next_id = -1 |
3270 | 168 | 178 | ||
3271 | 169 | def _move_to_login_page(self): | 179 | def _move_to_login_page(self): |
3272 | 170 | """Move to the login page wizard.""" | 180 | """Move to the login page wizard.""" |
3273 | 181 | logger.debug('Moving to CurrentUserSignInPage from: %s', | ||
3274 | 182 | self.currentPage()) | ||
3275 | 171 | self._next_id = self.current_user_page_id | 183 | self._next_id = self.current_user_page_id |
3276 | 172 | self.next() | 184 | self.next() |
3277 | 173 | self._next_id = -1 | 185 | self._next_id = -1 |
3278 | 174 | 186 | ||
3279 | 175 | def _move_to_success_page(self): | 187 | def _move_to_success_page(self): |
3280 | 176 | """Move to the success page wizard.""" | 188 | """Move to the success page wizard.""" |
3281 | 189 | logger.debug('Moving to SuccessPage from: %s', | ||
3282 | 190 | self.currentPage()) | ||
3283 | 177 | self._next_id = self.success_page_id | 191 | self._next_id = self.success_page_id |
3284 | 178 | self.next() | 192 | self.next() |
3285 | 179 | self.setButtonLayout([ | 193 | self.setButtonLayout([ |
3286 | @@ -186,6 +200,8 @@ | |||
3287 | 186 | 200 | ||
3288 | 187 | def _move_to_forgotten_page(self): | 201 | def _move_to_forgotten_page(self): |
3289 | 188 | """Move to the forgotten page wizard.""" | 202 | """Move to the forgotten page wizard.""" |
3290 | 203 | logger.debug('Moving to ForgottenPasswordPage from: %s', | ||
3291 | 204 | self.currentPage()) | ||
3292 | 189 | self._next_id = self.forgotten_password_page_id | 205 | self._next_id = self.forgotten_password_page_id |
3293 | 190 | self.next() | 206 | self.next() |
3294 | 191 | self._next_id = -1 | 207 | self._next_id = -1 |
3295 | @@ -258,6 +274,7 @@ | |||
3296 | 258 | logger.debug('UbuntuSSOClientGUI: app_name %r, kwargs %r.', | 274 | logger.debug('UbuntuSSOClientGUI: app_name %r, kwargs %r.', |
3297 | 259 | app_name, kwargs) | 275 | app_name, kwargs) |
3298 | 260 | self.app_name = app_name | 276 | self.app_name = app_name |
3299 | 277 | self.setWindowTitle(WINDOW_TITLE) | ||
3300 | 261 | # create the controller and the ui, then set the cb and call the show | 278 | # create the controller and the ui, then set the cb and call the show |
3301 | 262 | # method so that we can work | 279 | # method so that we can work |
3302 | 263 | self.wizard = UbuntuSSOWizard(app_name=app_name, **kwargs) | 280 | self.wizard = UbuntuSSOWizard(app_name=app_name, **kwargs) |
3303 | 264 | 281 | ||
3304 | === modified file 'ubuntu_sso/utils/__init__.py' | |||
3305 | --- ubuntu_sso/utils/__init__.py 2012-02-17 20:48:27 +0000 | |||
3306 | +++ ubuntu_sso/utils/__init__.py 2012-03-20 16:10:09 +0000 | |||
3307 | @@ -32,8 +32,14 @@ | |||
3308 | 32 | 32 | ||
3309 | 33 | 33 | ||
3310 | 34 | logger = setup_logging("ubuntu_sso.utils") | 34 | logger = setup_logging("ubuntu_sso.utils") |
3311 | 35 | BIN_SUFFIX = 'bin' | ||
3312 | 35 | DATA_SUFFIX = 'data' | 36 | DATA_SUFFIX = 'data' |
3314 | 36 | BIN_SUFFIX = 'bin' | 37 | |
3315 | 38 | if sys.platform == "win32": | ||
3316 | 39 | from ubuntu_sso.utils import windows as source | ||
3317 | 40 | else: | ||
3318 | 41 | from ubuntu_sso.utils import linux as source | ||
3319 | 42 | PLATFORM_QSS = source.PLATFORM_QSS | ||
3320 | 37 | 43 | ||
3321 | 38 | 44 | ||
3322 | 39 | def _get_dir(dir_name, dir_constant): | 45 | def _get_dir(dir_name, dir_constant): |
3323 | @@ -88,8 +94,15 @@ | |||
3324 | 88 | found, return the value of the BIN_DIR. | 94 | found, return the value of the BIN_DIR. |
3325 | 89 | 95 | ||
3326 | 90 | """ | 96 | """ |
3328 | 91 | result = _get_dir(dir_name=BIN_SUFFIX, dir_constant='BIN_DIR') | 97 | # If sys is frozen, this is an .exe, and all binaries are in |
3329 | 98 | # the same place | ||
3330 | 99 | if getattr(sys, "frozen", None) is not None: | ||
3331 | 100 | exec_path = os.path.abspath(sys.executable) | ||
3332 | 101 | result = os.path.dirname(exec_path) | ||
3333 | 102 | else: | ||
3334 | 103 | result = _get_dir(dir_name=BIN_SUFFIX, dir_constant='BIN_DIR') | ||
3335 | 92 | assert result is not None, '%r dir can not be None.' % BIN_SUFFIX | 104 | assert result is not None, '%r dir can not be None.' % BIN_SUFFIX |
3336 | 105 | logger.info('get_bin_dir: returning dir located at %r.', result) | ||
3337 | 93 | return result | 106 | return result |
3338 | 94 | 107 | ||
3339 | 95 | 108 | ||
3340 | 96 | 109 | ||
3341 | === added file 'ubuntu_sso/utils/linux.py' | |||
3342 | --- ubuntu_sso/utils/linux.py 1970-01-01 00:00:00 +0000 | |||
3343 | +++ ubuntu_sso/utils/linux.py 2012-03-20 16:10:09 +0000 | |||
3344 | @@ -0,0 +1,19 @@ | |||
3345 | 1 | # -*- coding: utf-8 -*- | ||
3346 | 2 | # | ||
3347 | 3 | # Copyright 2010-2012 Canonical Ltd. | ||
3348 | 4 | # | ||
3349 | 5 | # This program is free software: you can redistribute it and/or modify it | ||
3350 | 6 | # under the terms of the GNU General Public License version 3, as published | ||
3351 | 7 | # by the Free Software Foundation. | ||
3352 | 8 | # | ||
3353 | 9 | # This program is distributed in the hope that it will be useful, but | ||
3354 | 10 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
3355 | 11 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
3356 | 12 | # PURPOSE. See the GNU General Public License for more details. | ||
3357 | 13 | # | ||
3358 | 14 | # You should have received a copy of the GNU General Public License along | ||
3359 | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3360 | 16 | |||
3361 | 17 | """Platform specific constants and functions (for Linux).""" | ||
3362 | 18 | |||
3363 | 19 | PLATFORM_QSS = ":/linux.qss" | ||
3364 | 0 | 20 | ||
3365 | === modified file 'ubuntu_sso/utils/runner/glib.py' | |||
3366 | --- ubuntu_sso/utils/runner/glib.py 2012-02-17 18:43:17 +0000 | |||
3367 | +++ ubuntu_sso/utils/runner/glib.py 2012-03-20 16:10:09 +0000 | |||
3368 | @@ -27,7 +27,7 @@ | |||
3369 | 27 | logger = setup_logging("ubuntu_sso.utils.runner.glib") | 27 | logger = setup_logging("ubuntu_sso.utils.runner.glib") |
3370 | 28 | 28 | ||
3371 | 29 | 29 | ||
3373 | 30 | NO_SUCH_FILE_OR_DIR = 'No such file or directory' | 30 | NO_SUCH_FILE_OR_DIR = '[Errno 2]' |
3374 | 31 | 31 | ||
3375 | 32 | 32 | ||
3376 | 33 | def spawn_program(args, reply_handler, error_handler): | 33 | def spawn_program(args, reply_handler, error_handler): |
3377 | 34 | 34 | ||
3378 | === modified file 'ubuntu_sso/utils/runner/tx.py' | |||
3379 | --- ubuntu_sso/utils/runner/tx.py 2012-02-17 19:13:15 +0000 | |||
3380 | +++ ubuntu_sso/utils/runner/tx.py 2012-03-20 16:10:09 +0000 | |||
3381 | @@ -26,7 +26,7 @@ | |||
3382 | 26 | 26 | ||
3383 | 27 | logger = setup_logging("ubuntu_sso.utils.runner.tx") | 27 | logger = setup_logging("ubuntu_sso.utils.runner.tx") |
3384 | 28 | 28 | ||
3386 | 29 | NO_SUCH_FILE_OR_DIR = 'OSError: [Errno 2] No such file or directory' | 29 | NO_SUCH_FILE_OR_DIR = 'OSError: [Errno 2]' |
3387 | 30 | 30 | ||
3388 | 31 | 31 | ||
3389 | 32 | EXE_EXT = '' | 32 | EXE_EXT = '' |
3390 | 33 | 33 | ||
3391 | === modified file 'ubuntu_sso/utils/tests/test_common.py' | |||
3392 | --- ubuntu_sso/utils/tests/test_common.py 2012-02-15 20:21:52 +0000 | |||
3393 | +++ ubuntu_sso/utils/tests/test_common.py 2012-03-20 16:10:09 +0000 | |||
3394 | @@ -17,6 +17,7 @@ | |||
3395 | 17 | """Tests for the oauth_headers helper function.""" | 17 | """Tests for the oauth_headers helper function.""" |
3396 | 18 | 18 | ||
3397 | 19 | import logging | 19 | import logging |
3398 | 20 | import os | ||
3399 | 20 | import sys | 21 | import sys |
3400 | 21 | import time | 22 | import time |
3401 | 22 | 23 | ||
3402 | @@ -146,13 +147,21 @@ | |||
3403 | 146 | self.assertEqual(expected, result) | 147 | self.assertEqual(expected, result) |
3404 | 147 | 148 | ||
3405 | 148 | 149 | ||
3407 | 149 | class GetBinDirTestCase(TestCase): | 150 | class GetBinDirTestCase(GetProjectDirTestCase): |
3408 | 150 | """Test case for get_bin_dir when constants module is not defined.""" | 151 | """Test case for get_bin_dir when constants module is not defined.""" |
3409 | 151 | 152 | ||
3410 | 152 | DIR_NAME = utils.BIN_SUFFIX | 153 | DIR_NAME = utils.BIN_SUFFIX |
3411 | 153 | DIR_CONSTANT = 'BIN_DIR' | 154 | DIR_CONSTANT = 'BIN_DIR' |
3412 | 154 | DIR_GETTER = 'get_bin_dir' | 155 | DIR_GETTER = 'get_bin_dir' |
3413 | 155 | 156 | ||
3414 | 157 | def test_frozen_binary(self): | ||
3415 | 158 | """Test that frozen binaries return a valid path.""" | ||
3416 | 159 | sys.frozen = True | ||
3417 | 160 | self.addCleanup(delattr, sys, "frozen") | ||
3418 | 161 | expected = os.path.dirname(os.path.abspath(sys.executable)) | ||
3419 | 162 | result = self.get_dir() | ||
3420 | 163 | self.assertEqual(expected, result) | ||
3421 | 164 | |||
3422 | 156 | 165 | ||
3423 | 157 | class GetBinDirWithConstantsTestCase(GetProjectDirWithConstantsTestCase): | 166 | class GetBinDirWithConstantsTestCase(GetProjectDirWithConstantsTestCase): |
3424 | 158 | """Test case for get_bin_dir when constants module is not defined.""" | 167 | """Test case for get_bin_dir when constants module is not defined.""" |
3425 | 159 | 168 | ||
3426 | === modified file 'ubuntu_sso/utils/ui.py' | |||
3427 | --- ubuntu_sso/utils/ui.py 2012-03-06 14:13:10 +0000 | |||
3428 | +++ ubuntu_sso/utils/ui.py 2012-03-20 16:10:09 +0000 | |||
3429 | @@ -43,7 +43,7 @@ | |||
3430 | 43 | CAPTCHA_RELOAD_TEXT = _('refresh') | 43 | CAPTCHA_RELOAD_TEXT = _('refresh') |
3431 | 44 | CAPTCHA_RELOAD_TOOLTIP = _('Reload') | 44 | CAPTCHA_RELOAD_TOOLTIP = _('Reload') |
3432 | 45 | CAPTCHA_REQUIRED_ERROR = _('The captcha is a required field') | 45 | CAPTCHA_REQUIRED_ERROR = _('The captcha is a required field') |
3434 | 46 | CLOSE_AND_SETUP_LATER = _('Close window and setup later') | 46 | CLOSE_AND_SETUP_LATER = _('Close window and set up later') |
3435 | 47 | CONGRATULATIONS = _("Congratulations, {app_name} is installed!") | 47 | CONGRATULATIONS = _("Congratulations, {app_name} is installed!") |
3436 | 48 | CONNECT_HELP_LABEL = _('To connect this computer to %(app_name)s enter your ' | 48 | CONNECT_HELP_LABEL = _('To connect this computer to %(app_name)s enter your ' |
3437 | 49 | 'details below.') | 49 | 'details below.') |
3438 | @@ -133,6 +133,12 @@ | |||
3439 | 133 | SSL_CERT_DETAILS = _('Certificate details') | 133 | SSL_CERT_DETAILS = _('Certificate details') |
3440 | 134 | SSL_CONNECT_BUTTON = _('Connect') | 134 | SSL_CONNECT_BUTTON = _('Connect') |
3441 | 135 | SSL_DETAILS_HELP = _('the details ssl certificate we are going to show.') | 135 | SSL_DETAILS_HELP = _('the details ssl certificate we are going to show.') |
3442 | 136 | SSL_DETAILS_TEMPLATE = ('Organization:\t%(organization)s\n' | ||
3443 | 137 | 'Common Name:\t%(common_name)s\n' | ||
3444 | 138 | 'Locality Name:\t%(locality_name)s\n' | ||
3445 | 139 | 'Unit:\t%(unit)s\n' | ||
3446 | 140 | 'Country:\t%(country_name)s\n' | ||
3447 | 141 | 'State or Province:\t%(state_name)s') | ||
3448 | 136 | SSL_DESCRIPTION = _('Open the SSL certificate UI.') | 142 | SSL_DESCRIPTION = _('Open the SSL certificate UI.') |
3449 | 137 | SSL_DIALOG_TITLE = _('SSL Certificate Not Valid') | 143 | SSL_DIALOG_TITLE = _('SSL Certificate Not Valid') |
3450 | 138 | SSL_DOMAIN_HELP = _('the domain whose ssl certificate we are going to show.') | 144 | SSL_DOMAIN_HELP = _('the domain whose ssl certificate we are going to show.') |
3451 | 139 | 145 | ||
3452 | === modified file 'ubuntu_sso/utils/webclient/__init__.py' | |||
3453 | --- ubuntu_sso/utils/webclient/__init__.py 2012-02-09 18:11:02 +0000 | |||
3454 | +++ ubuntu_sso/utils/webclient/__init__.py 2012-03-20 16:10:09 +0000 | |||
3455 | @@ -19,6 +19,7 @@ | |||
3456 | 19 | 19 | ||
3457 | 20 | # pylint: disable=W0611 | 20 | # pylint: disable=W0611 |
3458 | 21 | from ubuntu_sso.utils.webclient.common import ( | 21 | from ubuntu_sso.utils.webclient.common import ( |
3459 | 22 | ProxyUnauthorizedError, | ||
3460 | 22 | UnauthorizedError, | 23 | UnauthorizedError, |
3461 | 23 | WebClientError, | 24 | WebClientError, |
3462 | 24 | ) | 25 | ) |
3463 | @@ -26,8 +27,23 @@ | |||
3464 | 26 | 27 | ||
3465 | 27 | def is_qt4reactor_installed(): | 28 | def is_qt4reactor_installed(): |
3466 | 28 | """Check if the qt4reactor is installed.""" | 29 | """Check if the qt4reactor is installed.""" |
3469 | 29 | reactor = sys.modules.get("twisted.internet.reactor") | 30 | result = False |
3470 | 30 | return reactor and getattr(reactor, "qApp", None) | 31 | |
3471 | 32 | if not 'PyQt4' in sys.modules: | ||
3472 | 33 | return result | ||
3473 | 34 | |||
3474 | 35 | try: | ||
3475 | 36 | from PyQt4.QtCore import QCoreApplication | ||
3476 | 37 | from PyQt4.QtGui import QApplication | ||
3477 | 38 | |||
3478 | 39 | # we could be running a process with or without ui, and those are diff | ||
3479 | 40 | # apps. | ||
3480 | 41 | result = (QCoreApplication.instance() is not None | ||
3481 | 42 | or QApplication.instance() is not None) | ||
3482 | 43 | except ImportError: | ||
3483 | 44 | pass | ||
3484 | 45 | |||
3485 | 46 | return result | ||
3486 | 31 | 47 | ||
3487 | 32 | 48 | ||
3488 | 33 | def webclient_module(): | 49 | def webclient_module(): |
3489 | 34 | 50 | ||
3490 | === modified file 'ubuntu_sso/utils/webclient/common.py' | |||
3491 | --- ubuntu_sso/utils/webclient/common.py 2012-02-09 18:11:02 +0000 | |||
3492 | +++ ubuntu_sso/utils/webclient/common.py 2012-03-20 16:10:09 +0000 | |||
3493 | @@ -17,14 +17,23 @@ | |||
3494 | 17 | 17 | ||
3495 | 18 | import cgi | 18 | import cgi |
3496 | 19 | import collections | 19 | import collections |
3497 | 20 | import os | ||
3498 | 20 | 21 | ||
3499 | 21 | from httplib2 import iri2uri | 22 | from httplib2 import iri2uri |
3500 | 22 | from oauth import oauth | 23 | from oauth import oauth |
3501 | 23 | from twisted.internet import defer | 24 | from twisted.internet import defer |
3502 | 24 | from urlparse import urlparse | 25 | from urlparse import urlparse |
3503 | 25 | 26 | ||
3504 | 27 | from ubuntu_sso import USER_SUCCESS, UI_PROXY_CREDS_DIALOG | ||
3505 | 28 | from ubuntu_sso.logger import setup_logging | ||
3506 | 29 | from ubuntu_sso.utils.runner import spawn_program | ||
3507 | 30 | from ubuntu_sso.utils.ui import SSL_DETAILS_TEMPLATE | ||
3508 | 26 | from ubuntu_sso.utils.webclient.timestamp import TimestampChecker | 31 | from ubuntu_sso.utils.webclient.timestamp import TimestampChecker |
3509 | 27 | 32 | ||
3510 | 33 | SSL_DIALOG = 'ubuntu-sso-ssl-certificate-qt' | ||
3511 | 34 | |||
3512 | 35 | logger = setup_logging("ubuntu_sso.utils.webclient.common") | ||
3513 | 36 | |||
3514 | 28 | 37 | ||
3515 | 29 | class WebClientError(Exception): | 38 | class WebClientError(Exception): |
3516 | 30 | """An http error happened while calling the webservice.""" | 39 | """An http error happened while calling the webservice.""" |
3517 | @@ -34,8 +43,12 @@ | |||
3518 | 34 | """The request ended with bad_request, unauthorized or forbidden.""" | 43 | """The request ended with bad_request, unauthorized or forbidden.""" |
3519 | 35 | 44 | ||
3520 | 36 | 45 | ||
3521 | 46 | class ProxyUnauthorizedError(WebClientError): | ||
3522 | 47 | """Failure raised when there is an issue with the proxy auth.""" | ||
3523 | 48 | |||
3524 | 49 | |||
3525 | 37 | class Response(object): | 50 | class Response(object): |
3527 | 38 | """A reponse object.""" | 51 | """A response object.""" |
3528 | 39 | 52 | ||
3529 | 40 | def __init__(self, content, headers=None): | 53 | def __init__(self, content, headers=None): |
3530 | 41 | """Initialize this instance.""" | 54 | """Initialize this instance.""" |
3531 | @@ -77,10 +90,14 @@ | |||
3532 | 77 | 90 | ||
3533 | 78 | timestamp_checker = None | 91 | timestamp_checker = None |
3534 | 79 | 92 | ||
3536 | 80 | def __init__(self, username=None, password=None, oauth_sign_plain=False): | 93 | def __init__(self, appname='', username=None, password=None, |
3537 | 94 | oauth_sign_plain=False): | ||
3538 | 81 | """Initialize this instance.""" | 95 | """Initialize this instance.""" |
3539 | 96 | self.appname = appname | ||
3540 | 82 | self.username = username | 97 | self.username = username |
3541 | 83 | self.password = password | 98 | self.password = password |
3542 | 99 | self.proxy_username = None | ||
3543 | 100 | self.proxy_password = None | ||
3544 | 84 | self.oauth_sign_plain = oauth_sign_plain | 101 | self.oauth_sign_plain = oauth_sign_plain |
3545 | 85 | 102 | ||
3546 | 86 | def request(self, iri, method="GET", extra_headers=None, | 103 | def request(self, iri, method="GET", extra_headers=None, |
3547 | @@ -154,3 +171,94 @@ | |||
3548 | 154 | 171 | ||
3549 | 155 | def shutdown(self): | 172 | def shutdown(self): |
3550 | 156 | """Shut down all pending requests (if possible).""" | 173 | """Shut down all pending requests (if possible).""" |
3551 | 174 | |||
3552 | 175 | @defer.inlineCallbacks | ||
3553 | 176 | def _load_proxy_creds_from_keyring(self, domain): | ||
3554 | 177 | """Load the proxy creds from the keyring.""" | ||
3555 | 178 | from ubuntu_sso.keyring import Keyring | ||
3556 | 179 | keyring = Keyring() | ||
3557 | 180 | try: | ||
3558 | 181 | creds = yield keyring.get_credentials(str(domain)) | ||
3559 | 182 | logger.debug('Got credentials from keyring.') | ||
3560 | 183 | except Exception, e: | ||
3561 | 184 | logger.error('Error when retrieving the creds.') | ||
3562 | 185 | raise WebClientError('Error when retrieving the creds.', e) | ||
3563 | 186 | if creds is not None: | ||
3564 | 187 | # if we are loading the same creds it means that we got the wrong | ||
3565 | 188 | # ones | ||
3566 | 189 | if (self.proxy_username == creds['username'] and | ||
3567 | 190 | self.proxy_password == creds['password']): | ||
3568 | 191 | defer.returnValue(False) | ||
3569 | 192 | else: | ||
3570 | 193 | self.proxy_username = creds['username'] | ||
3571 | 194 | self.proxy_password = creds['password'] | ||
3572 | 195 | defer.returnValue(True) | ||
3573 | 196 | logger.debug('Proxy creds not in keyring.') | ||
3574 | 197 | defer.returnValue(False) | ||
3575 | 198 | |||
3576 | 199 | def _launch_proxy_creds_dialog(self, domain, retry): | ||
3577 | 200 | """Launch the dialog used to get the creds.""" | ||
3578 | 201 | from ubuntu_sso.utils import get_bin_dir | ||
3579 | 202 | |||
3580 | 203 | bin_dir = get_bin_dir() | ||
3581 | 204 | args = (os.path.join(bin_dir, UI_PROXY_CREDS_DIALOG), '--domain', | ||
3582 | 205 | domain) | ||
3583 | 206 | if retry: | ||
3584 | 207 | args += ('--retry',) | ||
3585 | 208 | return spawn_program(args) | ||
3586 | 209 | |||
3587 | 210 | @defer.inlineCallbacks | ||
3588 | 211 | def request_proxy_auth_credentials(self, domain, retry): | ||
3589 | 212 | """Request the auth creds to the user.""" | ||
3590 | 213 | if not retry: | ||
3591 | 214 | if (self.proxy_username is not None | ||
3592 | 215 | and self.proxy_password is not None): | ||
3593 | 216 | logger.debug('Not retry and credentials are present.') | ||
3594 | 217 | defer.returnValue(True) | ||
3595 | 218 | else: | ||
3596 | 219 | creds_loaded = yield self._load_proxy_creds_from_keyring( | ||
3597 | 220 | domain) | ||
3598 | 221 | if creds_loaded: | ||
3599 | 222 | defer.returnValue(True) | ||
3600 | 223 | |||
3601 | 224 | try: | ||
3602 | 225 | return_code = yield self._launch_proxy_creds_dialog(domain, retry) | ||
3603 | 226 | except Exception, e: | ||
3604 | 227 | logger.error('Error when running external ui process.') | ||
3605 | 228 | raise WebClientError('Error when running external ui process.', e) | ||
3606 | 229 | |||
3607 | 230 | if return_code == USER_SUCCESS: | ||
3608 | 231 | creds_loaded = yield self._load_proxy_creds_from_keyring(domain) | ||
3609 | 232 | defer.returnValue(creds_loaded) | ||
3610 | 233 | else: | ||
3611 | 234 | logger.debug('Could not retrieve the credentials. Return code: %r', | ||
3612 | 235 | return_code) | ||
3613 | 236 | defer.returnValue(False) | ||
3614 | 237 | |||
3615 | 238 | def format_ssl_details(self, details): | ||
3616 | 239 | """Return a formatted string with the details.""" | ||
3617 | 240 | return SSL_DETAILS_TEMPLATE % details | ||
3618 | 241 | |||
3619 | 242 | def _launch_ssl_dialog(self, domain, details): | ||
3620 | 243 | """Launch a dialog used to approve the ssl cert.""" | ||
3621 | 244 | from ubuntu_sso.utils import get_bin_dir | ||
3622 | 245 | |||
3623 | 246 | bin_dir = get_bin_dir() | ||
3624 | 247 | args = (os.path.join(bin_dir, SSL_DIALOG), '--domain', domain, | ||
3625 | 248 | '--details', details, | ||
3626 | 249 | '--appname', self.appname) | ||
3627 | 250 | return spawn_program(args) | ||
3628 | 251 | |||
3629 | 252 | def _was_ssl_accepted(self, cert_details): | ||
3630 | 253 | """Return if the cert was already accepted.""" | ||
3631 | 254 | # TODO: Ensure that we look at pinned certs in a following branch | ||
3632 | 255 | return False | ||
3633 | 256 | |||
3634 | 257 | @defer.inlineCallbacks | ||
3635 | 258 | def request_ssl_cert_approval(self, domain, details): | ||
3636 | 259 | """Request the user for ssl approval.""" | ||
3637 | 260 | if self._was_ssl_accepted(details): | ||
3638 | 261 | defer.returnValue(True) | ||
3639 | 262 | |||
3640 | 263 | return_code = yield self._launch_ssl_dialog(domain, details) | ||
3641 | 264 | defer.returnValue(return_code == USER_SUCCESS) | ||
3642 | 157 | 265 | ||
3643 | === modified file 'ubuntu_sso/utils/webclient/gsettings.py' | |||
3644 | --- ubuntu_sso/utils/webclient/gsettings.py 2012-02-10 14:46:16 +0000 | |||
3645 | +++ ubuntu_sso/utils/webclient/gsettings.py 2012-03-20 16:10:09 +0000 | |||
3646 | @@ -31,6 +31,24 @@ | |||
3647 | 31 | return hostname, username, password | 31 | return hostname, username, password |
3648 | 32 | 32 | ||
3649 | 33 | 33 | ||
3650 | 34 | def parse_manual_proxy_settings(scheme, gsettings): | ||
3651 | 35 | """Parse the settings for a given scheme.""" | ||
3652 | 36 | host, username, pwd = parse_proxy_host(gsettings[scheme + ".host"]) | ||
3653 | 37 | settings = { | ||
3654 | 38 | "host": host, | ||
3655 | 39 | "port": gsettings[scheme + ".port"], | ||
3656 | 40 | } | ||
3657 | 41 | if scheme == "http" and gsettings["http.use-authentication"]: | ||
3658 | 42 | username = gsettings["http.authentication-user"] | ||
3659 | 43 | pwd = gsettings["http.authentication-password"] | ||
3660 | 44 | if username is not None and pwd is not None: | ||
3661 | 45 | settings.update({ | ||
3662 | 46 | "username": username, | ||
3663 | 47 | "password": pwd, | ||
3664 | 48 | }) | ||
3665 | 49 | return settings | ||
3666 | 50 | |||
3667 | 51 | |||
3668 | 34 | def get_proxy_settings(): | 52 | def get_proxy_settings(): |
3669 | 35 | """Parse the proxy settings as returned by the gsettings executable.""" | 53 | """Parse the proxy settings as returned by the gsettings executable.""" |
3670 | 36 | output = subprocess.check_output(GSETTINGS_CMDLINE.split()) | 54 | output = subprocess.check_output(GSETTINGS_CMDLINE.split()) |
3671 | @@ -56,20 +74,9 @@ | |||
3672 | 56 | if mode == "none": | 74 | if mode == "none": |
3673 | 57 | settings = {} | 75 | settings = {} |
3674 | 58 | elif mode == "manual": | 76 | elif mode == "manual": |
3689 | 59 | # attempt to parse the host | 77 | settings = {} |
3690 | 60 | host, username, pwd = parse_proxy_host(gsettings["http.host"]) | 78 | for scheme in ["http", "https"]: |
3691 | 61 | settings = { | 79 | settings[scheme] = parse_manual_proxy_settings(scheme, gsettings) |
3678 | 62 | "host": host, | ||
3679 | 63 | "port": gsettings["http.port"], | ||
3680 | 64 | } | ||
3681 | 65 | if gsettings["http.use-authentication"]: | ||
3682 | 66 | username = gsettings["http.authentication-user"] | ||
3683 | 67 | pwd = gsettings["http.authentication-password"] | ||
3684 | 68 | if username is not None and pwd is not None: | ||
3685 | 69 | settings.update({ | ||
3686 | 70 | "username": username, | ||
3687 | 71 | "password": pwd, | ||
3688 | 72 | }) | ||
3692 | 73 | else: | 80 | else: |
3693 | 74 | # If mode is automatic the PAC javascript should be interpreted | 81 | # If mode is automatic the PAC javascript should be interpreted |
3694 | 75 | # on each request. That is out of scope so it's ignored for now | 82 | # on each request. That is out of scope so it's ignored for now |
3695 | 76 | 83 | ||
3696 | === modified file 'ubuntu_sso/utils/webclient/libsoup.py' | |||
3697 | --- ubuntu_sso/utils/webclient/libsoup.py 2012-02-07 19:36:50 +0000 | |||
3698 | +++ ubuntu_sso/utils/webclient/libsoup.py 2012-03-20 16:10:09 +0000 | |||
3699 | @@ -19,10 +19,12 @@ | |||
3700 | 19 | 19 | ||
3701 | 20 | from twisted.internet import defer | 20 | from twisted.internet import defer |
3702 | 21 | 21 | ||
3703 | 22 | from ubuntu_sso.logger import setup_logging | ||
3704 | 22 | from ubuntu_sso.utils.webclient.common import ( | 23 | from ubuntu_sso.utils.webclient.common import ( |
3705 | 23 | BaseWebClient, | 24 | BaseWebClient, |
3706 | 24 | HeaderDict, | 25 | HeaderDict, |
3707 | 25 | Response, | 26 | Response, |
3708 | 27 | ProxyUnauthorizedError, | ||
3709 | 26 | UnauthorizedError, | 28 | UnauthorizedError, |
3710 | 27 | WebClientError, | 29 | WebClientError, |
3711 | 28 | ) | 30 | ) |
3712 | @@ -30,6 +32,8 @@ | |||
3713 | 30 | URI_ANONYMOUS_TEMPLATE = "http://{host}:{port}/" | 32 | URI_ANONYMOUS_TEMPLATE = "http://{host}:{port}/" |
3714 | 31 | URI_USERNAME_TEMPLATE = "http://{username}:{password}@{host}:{port}/" | 33 | URI_USERNAME_TEMPLATE = "http://{username}:{password}@{host}:{port}/" |
3715 | 32 | 34 | ||
3716 | 35 | logger = setup_logging("ubuntu_sso.utils.webclient.libsoup") | ||
3717 | 36 | |||
3718 | 33 | 37 | ||
3719 | 34 | class WebClient(BaseWebClient): | 38 | class WebClient(BaseWebClient): |
3720 | 35 | """A webclient with a libsoup backend.""" | 39 | """A webclient with a libsoup backend.""" |
3721 | @@ -41,11 +45,12 @@ | |||
3722 | 41 | from gi.repository import Soup, SoupGNOME | 45 | from gi.repository import Soup, SoupGNOME |
3723 | 42 | self.soup = Soup | 46 | self.soup = Soup |
3724 | 43 | self.session = Soup.SessionAsync() | 47 | self.session = Soup.SessionAsync() |
3726 | 44 | self.session.add_feature_by_type(SoupGNOME.ProxyResolverGNOME) | 48 | self.session.add_feature(SoupGNOME.ProxyResolverGNOME()) |
3727 | 45 | self.session.connect("authenticate", self._on_authenticate) | 49 | self.session.connect("authenticate", self._on_authenticate) |
3728 | 46 | 50 | ||
3729 | 47 | def _on_message(self, session, message, d): | 51 | def _on_message(self, session, message, d): |
3730 | 48 | """Handle the result of an http message.""" | 52 | """Handle the result of an http message.""" |
3731 | 53 | logger.debug('_on_message status code is %s', message.status_code) | ||
3732 | 49 | if message.status_code == httplib.OK: | 54 | if message.status_code == httplib.OK: |
3733 | 50 | headers = HeaderDict() | 55 | headers = HeaderDict() |
3734 | 51 | response_headers = message.get_property("response-headers") | 56 | response_headers = message.get_property("response-headers") |
3735 | @@ -57,14 +62,51 @@ | |||
3736 | 57 | elif message.status_code == httplib.UNAUTHORIZED: | 62 | elif message.status_code == httplib.UNAUTHORIZED: |
3737 | 58 | e = UnauthorizedError(message.reason_phrase) | 63 | e = UnauthorizedError(message.reason_phrase) |
3738 | 59 | d.errback(e) | 64 | d.errback(e) |
3739 | 65 | elif message.status_code == httplib.PROXY_AUTHENTICATION_REQUIRED: | ||
3740 | 66 | e = ProxyUnauthorizedError(message.reason_phrase) | ||
3741 | 67 | d.errback(e) | ||
3742 | 60 | else: | 68 | else: |
3743 | 61 | e = WebClientError(message.reason_phrase) | 69 | e = WebClientError(message.reason_phrase) |
3744 | 62 | d.errback(e) | 70 | d.errback(e) |
3745 | 63 | 71 | ||
3747 | 64 | def _on_authenticate(self, sesion, message, auth, retrying, data=None): | 72 | @defer.inlineCallbacks |
3748 | 73 | def _on_authenticate(self, session, message, auth, retrying, data=None): | ||
3749 | 65 | """Handle the "authenticate" signal.""" | 74 | """Handle the "authenticate" signal.""" |
3752 | 66 | if not retrying and self.username and self.password: | 75 | self.session.pause_message(message) |
3753 | 67 | auth.authenticate(self.username, self.password) | 76 | try: |
3754 | 77 | logger.debug('_on_authenticate: message status code is %s', | ||
3755 | 78 | message.status_code) | ||
3756 | 79 | if not retrying and self.username and self.password: | ||
3757 | 80 | auth.authenticate(self.username, self.password) | ||
3758 | 81 | if auth.is_for_proxy(): | ||
3759 | 82 | logger.debug('_on_authenticate auth is for proxy.') | ||
3760 | 83 | got_creds = yield self.request_proxy_auth_credentials( | ||
3761 | 84 | self.session.props.proxy_uri.host, | ||
3762 | 85 | retrying) | ||
3763 | 86 | if got_creds: | ||
3764 | 87 | logger.debug('Got proxy credentials from user.') | ||
3765 | 88 | auth.authenticate(self.proxy_username, self.proxy_password) | ||
3766 | 89 | finally: | ||
3767 | 90 | self.session.unpause_message(message) | ||
3768 | 91 | |||
3769 | 92 | @defer.inlineCallbacks | ||
3770 | 93 | def _on_proxy_authenticate(self, failure, iri, method="GET", | ||
3771 | 94 | extra_headers=None, oauth_credentials=None, post_content=None): | ||
3772 | 95 | """Deal with wrong settings.""" | ||
3773 | 96 | failure.trap(ProxyUnauthorizedError) | ||
3774 | 97 | logger.debug('Proxy settings are wrong.') | ||
3775 | 98 | got_creds = yield self.request_proxy_auth_credentials( | ||
3776 | 99 | self.session.props.proxy_uri.host, | ||
3777 | 100 | True) | ||
3778 | 101 | if got_creds: | ||
3779 | 102 | settings = dict(host=self.session.props.proxy_uri.host, | ||
3780 | 103 | port=self.session.props.proxy_uri.port, | ||
3781 | 104 | username=self.proxy_username, | ||
3782 | 105 | password=self.proxy_password) | ||
3783 | 106 | self.force_use_proxy(settings) | ||
3784 | 107 | response = yield self.request(iri, method, extra_headers, | ||
3785 | 108 | oauth_credentials, post_content) | ||
3786 | 109 | defer.returnValue(response) | ||
3787 | 68 | 110 | ||
3788 | 69 | @defer.inlineCallbacks | 111 | @defer.inlineCallbacks |
3789 | 70 | def request(self, iri, method="GET", extra_headers=None, | 112 | def request(self, iri, method="GET", extra_headers=None, |
3790 | @@ -92,6 +134,8 @@ | |||
3791 | 92 | message.request_body.append(post_content) | 134 | message.request_body.append(post_content) |
3792 | 93 | 135 | ||
3793 | 94 | self.session.queue_message(message, self._on_message, d) | 136 | self.session.queue_message(message, self._on_message, d) |
3794 | 137 | d.addErrback(self._on_proxy_authenticate, iri, method, extra_headers, | ||
3795 | 138 | oauth_credentials, post_content) | ||
3796 | 95 | response = yield d | 139 | response = yield d |
3797 | 96 | defer.returnValue(response) | 140 | defer.returnValue(response) |
3798 | 97 | 141 | ||
3799 | 98 | 142 | ||
3800 | === modified file 'ubuntu_sso/utils/webclient/qtnetwork.py' | |||
3801 | --- ubuntu_sso/utils/webclient/qtnetwork.py 2012-02-07 19:36:50 +0000 | |||
3802 | +++ ubuntu_sso/utils/webclient/qtnetwork.py 2012-03-20 16:10:09 +0000 | |||
3803 | @@ -30,29 +30,61 @@ | |||
3804 | 30 | QNetworkProxyFactory, | 30 | QNetworkProxyFactory, |
3805 | 31 | QNetworkReply, | 31 | QNetworkReply, |
3806 | 32 | QNetworkRequest, | 32 | QNetworkRequest, |
3807 | 33 | QSslCertificate, | ||
3808 | 33 | ) | 34 | ) |
3809 | 34 | from twisted.internet import defer | 35 | from twisted.internet import defer |
3810 | 35 | 36 | ||
3811 | 37 | from ubuntu_sso.logger import setup_logging | ||
3812 | 36 | from ubuntu_sso.utils.webclient.common import ( | 38 | from ubuntu_sso.utils.webclient.common import ( |
3813 | 37 | BaseWebClient, | 39 | BaseWebClient, |
3814 | 38 | HeaderDict, | 40 | HeaderDict, |
3815 | 41 | ProxyUnauthorizedError, | ||
3816 | 39 | Response, | 42 | Response, |
3817 | 40 | UnauthorizedError, | 43 | UnauthorizedError, |
3818 | 41 | WebClientError, | 44 | WebClientError, |
3819 | 42 | ) | 45 | ) |
3820 | 43 | from ubuntu_sso.utils.webclient import gsettings | 46 | from ubuntu_sso.utils.webclient import gsettings |
3821 | 44 | 47 | ||
3822 | 48 | logger = setup_logging("ubuntu_sso.utils.webclient.qtnetwork") | ||
3823 | 49 | |||
3824 | 50 | |||
3825 | 51 | def build_proxy(settings_groups): | ||
3826 | 52 | """Create a QNetworkProxy from these settings.""" | ||
3827 | 53 | proxy_groups = [ | ||
3828 | 54 | ("socks", QNetworkProxy.Socks5Proxy), | ||
3829 | 55 | ("https", QNetworkProxy.HttpProxy), | ||
3830 | 56 | ("http", QNetworkProxy.HttpProxy), | ||
3831 | 57 | ] | ||
3832 | 58 | for group, proxy_type in proxy_groups: | ||
3833 | 59 | if group not in settings_groups: | ||
3834 | 60 | continue | ||
3835 | 61 | settings = settings_groups[group] | ||
3836 | 62 | if "host" in settings and "port" in settings: | ||
3837 | 63 | return QNetworkProxy(proxy_type, | ||
3838 | 64 | hostName=settings.get("host", ""), | ||
3839 | 65 | port=settings.get("port", 0), | ||
3840 | 66 | user=settings.get("username", ""), | ||
3841 | 67 | password=settings.get("password", "")) | ||
3842 | 68 | logger.error("No proxy correctly configured.") | ||
3843 | 69 | return QNetworkProxy(QNetworkProxy.DefaultProxy) | ||
3844 | 70 | |||
3845 | 45 | 71 | ||
3846 | 46 | class WebClient(BaseWebClient): | 72 | class WebClient(BaseWebClient): |
3847 | 47 | """A webclient with a qtnetwork backend.""" | 73 | """A webclient with a qtnetwork backend.""" |
3848 | 48 | 74 | ||
3849 | 75 | proxy_instance = None | ||
3850 | 76 | |||
3851 | 49 | def __init__(self, *args, **kwargs): | 77 | def __init__(self, *args, **kwargs): |
3852 | 50 | """Initialize this instance.""" | 78 | """Initialize this instance.""" |
3853 | 51 | super(WebClient, self).__init__(*args, **kwargs) | 79 | super(WebClient, self).__init__(*args, **kwargs) |
3854 | 52 | self.nam = QNetworkAccessManager(QCoreApplication.instance()) | 80 | self.nam = QNetworkAccessManager(QCoreApplication.instance()) |
3855 | 53 | self.nam.finished.connect(self._handle_finished) | 81 | self.nam.finished.connect(self._handle_finished) |
3856 | 54 | self.nam.authenticationRequired.connect(self._handle_authentication) | 82 | self.nam.authenticationRequired.connect(self._handle_authentication) |
3857 | 83 | self.nam.proxyAuthenticationRequired.connect(self.handle_proxy_auth) | ||
3858 | 84 | # Disabled until we make this a per-instance option | ||
3859 | 85 | #self.nam.sslErrors.connect(self._handle_ssl_errors) | ||
3860 | 55 | self.replies = {} | 86 | self.replies = {} |
3861 | 87 | self.proxy_retry = False | ||
3862 | 56 | self.setup_proxy() | 88 | self.setup_proxy() |
3863 | 57 | 89 | ||
3864 | 58 | def setup_proxy(self): | 90 | def setup_proxy(self): |
3865 | @@ -60,11 +92,43 @@ | |||
3866 | 60 | # QtNetwork knows how to use the system settings on both Win and Mac | 92 | # QtNetwork knows how to use the system settings on both Win and Mac |
3867 | 61 | if sys.platform.startswith("linux"): | 93 | if sys.platform.startswith("linux"): |
3868 | 62 | settings = gsettings.get_proxy_settings() | 94 | settings = gsettings.get_proxy_settings() |
3871 | 63 | if settings: | 95 | enabled = len(settings) > 0 |
3872 | 64 | self.force_use_proxy(settings) | 96 | if enabled and WebClient.proxy_instance is None: |
3873 | 97 | proxy = build_proxy(settings) | ||
3874 | 98 | QNetworkProxy.setApplicationProxy(proxy) | ||
3875 | 99 | WebClient.proxy_instance = proxy | ||
3876 | 100 | elif enabled and WebClient.proxy_instance: | ||
3877 | 101 | logger.info("Proxy already in use.") | ||
3878 | 102 | else: | ||
3879 | 103 | logger.info("Proxy is disabled.") | ||
3880 | 65 | else: | 104 | else: |
3881 | 66 | QNetworkProxyFactory.setUseSystemConfiguration(True) | 105 | QNetworkProxyFactory.setUseSystemConfiguration(True) |
3882 | 67 | 106 | ||
3883 | 107 | def handle_proxy_auth(self, proxy, authenticator): | ||
3884 | 108 | """Proxy authentication is required.""" | ||
3885 | 109 | logger.info("auth_required %r, %r", self.proxy_username, | ||
3886 | 110 | proxy.hostName()) | ||
3887 | 111 | if (self.proxy_username is not None and | ||
3888 | 112 | self.proxy_username != str(authenticator.user())): | ||
3889 | 113 | authenticator.setUser(self.proxy_username) | ||
3890 | 114 | WebClient.proxy_instance.setUser(self.proxy_username) | ||
3891 | 115 | if (self.proxy_password is not None and | ||
3892 | 116 | self.proxy_password != str(authenticator.password())): | ||
3893 | 117 | authenticator.setPassword(self.proxy_password) | ||
3894 | 118 | WebClient.proxy_instance.setPassword(self.proxy_password) | ||
3895 | 119 | |||
3896 | 120 | def _perform_request(self, request, method, post_buffer): | ||
3897 | 121 | """Return a deferred that will be fired with a Response object.""" | ||
3898 | 122 | d = defer.Deferred() | ||
3899 | 123 | if method == "GET": | ||
3900 | 124 | reply = self.nam.get(request) | ||
3901 | 125 | elif method == "HEAD": | ||
3902 | 126 | reply = self.nam.head(request) | ||
3903 | 127 | else: | ||
3904 | 128 | reply = self.nam.sendCustomRequest(request, method, post_buffer) | ||
3905 | 129 | self.replies[reply] = d | ||
3906 | 130 | return d | ||
3907 | 131 | |||
3908 | 68 | @defer.inlineCallbacks | 132 | @defer.inlineCallbacks |
3909 | 69 | def request(self, iri, method="GET", extra_headers=None, | 133 | def request(self, iri, method="GET", extra_headers=None, |
3910 | 70 | oauth_credentials=None, post_content=None): | 134 | oauth_credentials=None, post_content=None): |
3911 | @@ -86,23 +150,30 @@ | |||
3912 | 86 | for key, value in headers.iteritems(): | 150 | for key, value in headers.iteritems(): |
3913 | 87 | request.setRawHeader(key, value) | 151 | request.setRawHeader(key, value) |
3914 | 88 | 152 | ||
3926 | 89 | d = defer.Deferred() | 153 | post_buffer = QBuffer() |
3927 | 90 | if method == "GET": | 154 | post_buffer.setData(post_content) |
3928 | 91 | reply = self.nam.get(request) | 155 | try: |
3929 | 92 | elif method == "HEAD": | 156 | result = yield self._perform_request(request, method, post_buffer) |
3930 | 93 | reply = self.nam.head(request) | 157 | except ProxyUnauthorizedError, e: |
3931 | 94 | else: | 158 | app_proxy = QNetworkProxy.applicationProxy() |
3932 | 95 | post_buffer = QBuffer() | 159 | proxy_host = app_proxy.hostName() if app_proxy else "proxy server" |
3933 | 96 | post_buffer.setData(post_content) | 160 | got_creds = yield self.request_proxy_auth_credentials( |
3934 | 97 | reply = self.nam.sendCustomRequest(request, method, post_buffer) | 161 | proxy_host, self.proxy_retry) |
3935 | 98 | self.replies[reply] = d | 162 | if got_creds: |
3936 | 99 | result = yield d | 163 | self.proxy_retry = True |
3937 | 164 | result = yield self.request(iri, method, extra_headers, | ||
3938 | 165 | oauth_credentials, post_content) | ||
3939 | 166 | else: | ||
3940 | 167 | excp = WebClientError('Proxy creds needed.', e) | ||
3941 | 168 | defer.returnValue(excp) | ||
3942 | 100 | defer.returnValue(result) | 169 | defer.returnValue(result) |
3943 | 101 | 170 | ||
3944 | 102 | def _handle_authentication(self, reply, authenticator): | 171 | def _handle_authentication(self, reply, authenticator): |
3945 | 103 | """The reply needs authentication.""" | 172 | """The reply needs authentication.""" |
3948 | 104 | authenticator.setUser(self.username) | 173 | if authenticator.user() != self.username: |
3949 | 105 | authenticator.setPassword(self.password) | 174 | authenticator.setUser(self.username) |
3950 | 175 | if authenticator.password() != self.password: | ||
3951 | 176 | authenticator.setPassword(self.password) | ||
3952 | 106 | 177 | ||
3953 | 107 | def _handle_finished(self, reply): | 178 | def _handle_finished(self, reply): |
3954 | 108 | """The reply has finished processing.""" | 179 | """The reply has finished processing.""" |
3955 | @@ -118,20 +189,51 @@ | |||
3956 | 118 | d.callback(response) | 189 | d.callback(response) |
3957 | 119 | else: | 190 | else: |
3958 | 120 | error_string = reply.errorString() | 191 | error_string = reply.errorString() |
3959 | 192 | logger.debug('_handle_finished error (%s,%s).', error, | ||
3960 | 193 | error_string) | ||
3961 | 121 | if error == QNetworkReply.AuthenticationRequiredError: | 194 | if error == QNetworkReply.AuthenticationRequiredError: |
3962 | 122 | exception = UnauthorizedError(error_string, content) | 195 | exception = UnauthorizedError(error_string, content) |
3963 | 196 | elif error == QNetworkReply.ProxyAuthenticationRequiredError: | ||
3964 | 197 | # we are going thru a proxy and we did not auth | ||
3965 | 198 | exception = ProxyUnauthorizedError(error_string, content) | ||
3966 | 123 | else: | 199 | else: |
3967 | 124 | exception = WebClientError(error_string, content) | 200 | exception = WebClientError(error_string, content) |
3968 | 125 | d.errback(exception) | 201 | d.errback(exception) |
3969 | 126 | 202 | ||
3971 | 127 | def force_use_proxy(self, settings): | 203 | def _get_certificate_details(self, cert): |
3972 | 204 | """Return an string with the details of the certificate.""" | ||
3973 | 205 | detail_titles = {QSslCertificate.Organization: 'organization', | ||
3974 | 206 | QSslCertificate.CommonName: 'common_name', | ||
3975 | 207 | QSslCertificate.LocalityName: 'locality_name', | ||
3976 | 208 | QSslCertificate.OrganizationalUnitName: 'unit', | ||
3977 | 209 | QSslCertificate.CountryName: 'country_name', | ||
3978 | 210 | QSslCertificate.StateOrProvinceName: 'state_name'} | ||
3979 | 211 | details = {} | ||
3980 | 212 | for info, title in detail_titles.iteritems(): | ||
3981 | 213 | details[title] = str(cert.issuerInfo(info)) | ||
3982 | 214 | return self.format_ssl_details(details) | ||
3983 | 215 | |||
3984 | 216 | def _get_certificate_host(self, cert): | ||
3985 | 217 | """Return the host of the cert.""" | ||
3986 | 218 | return str(cert.issuerInfo(QSslCertificate.CommonName)) | ||
3987 | 219 | |||
3988 | 220 | @defer.inlineCallbacks | ||
3989 | 221 | def _handle_ssl_errors(self, reply, errors): | ||
3990 | 222 | """Handle the case in which we got an ssl error.""" | ||
3991 | 223 | # ask the user if the cer should be trusted | ||
3992 | 224 | cert = errors[0].certificate() | ||
3993 | 225 | trust_cert = yield self.request_ssl_cert_approval( | ||
3994 | 226 | self._get_certificate_host(cert), | ||
3995 | 227 | self._get_certificate_details(cert)) | ||
3996 | 228 | if trust_cert: | ||
3997 | 229 | reply.ignoreSslErrors() | ||
3998 | 230 | |||
3999 | 231 | def force_use_proxy(self, https_settings): | ||
4000 | 128 | """Setup this webclient to use the given proxy settings.""" | 232 | """Setup this webclient to use the given proxy settings.""" |
4007 | 129 | proxy = QNetworkProxy(QNetworkProxy.HttpProxy, | 233 | settings = {"https": https_settings} |
4008 | 130 | hostName=settings.get("host", ""), | 234 | proxy = build_proxy(settings) |
4009 | 131 | port=settings.get("port", 0), | 235 | QNetworkProxy.setApplicationProxy(proxy) |
4010 | 132 | user=settings.get("username", ""), | 236 | WebClient.proxy_instance = proxy |
4005 | 133 | password=settings.get("password", "")) | ||
4006 | 134 | self.nam.setProxy(proxy) | ||
4011 | 135 | 237 | ||
4012 | 136 | def shutdown(self): | 238 | def shutdown(self): |
4013 | 137 | """Shut down all pending requests (if possible).""" | 239 | """Shut down all pending requests (if possible).""" |
4014 | 138 | 240 | ||
4015 | === modified file 'ubuntu_sso/utils/webclient/tests/__init__.py' | |||
4016 | --- ubuntu_sso/utils/webclient/tests/__init__.py 2012-02-07 19:36:50 +0000 | |||
4017 | +++ ubuntu_sso/utils/webclient/tests/__init__.py 2012-03-20 16:10:09 +0000 | |||
4018 | @@ -17,9 +17,48 @@ | |||
4019 | 17 | """Tests for the proxy-aware webclient.""" | 17 | """Tests for the proxy-aware webclient.""" |
4020 | 18 | 18 | ||
4021 | 19 | from twisted.application import internet, service | 19 | from twisted.application import internet, service |
4022 | 20 | from twisted.internet import ssl | ||
4023 | 20 | from twisted.web import http, server | 21 | from twisted.web import http, server |
4024 | 21 | 22 | ||
4025 | 22 | 23 | ||
4026 | 24 | # Some settings are not used as described in: | ||
4027 | 25 | # https://bugzilla.gnome.org/show_bug.cgi?id=648237 | ||
4028 | 26 | |||
4029 | 27 | TEMPLATE_GSETTINGS_OUTPUT = """\ | ||
4030 | 28 | org.gnome.system.proxy autoconfig-url '{autoconfig_url}' | ||
4031 | 29 | org.gnome.system.proxy ignore-hosts {ignore_hosts:s} | ||
4032 | 30 | org.gnome.system.proxy mode '{mode}' | ||
4033 | 31 | org.gnome.system.proxy.ftp host '{ftp_host}' | ||
4034 | 32 | org.gnome.system.proxy.ftp port {ftp_port} | ||
4035 | 33 | org.gnome.system.proxy.http authentication-password '{auth_password}' | ||
4036 | 34 | org.gnome.system.proxy.http authentication-user '{auth_user}' | ||
4037 | 35 | org.gnome.system.proxy.http host '{http_host}' | ||
4038 | 36 | org.gnome.system.proxy.http port {http_port} | ||
4039 | 37 | org.gnome.system.proxy.http use-authentication {http_use_auth} | ||
4040 | 38 | org.gnome.system.proxy.https host '{https_host}' | ||
4041 | 39 | org.gnome.system.proxy.https port {https_port} | ||
4042 | 40 | org.gnome.system.proxy.socks host '{socks_host}' | ||
4043 | 41 | org.gnome.system.proxy.socks port {socks_port} | ||
4044 | 42 | """ | ||
4045 | 43 | |||
4046 | 44 | BASE_GSETTINGS_VALUES = { | ||
4047 | 45 | "autoconfig_url": "", | ||
4048 | 46 | "ignore_hosts": ["localhost", "127.0.0.0/8"], | ||
4049 | 47 | "mode": "none", | ||
4050 | 48 | "ftp_host": "", | ||
4051 | 49 | "ftp_port": 0, | ||
4052 | 50 | "auth_password": "", | ||
4053 | 51 | "auth_user": "", | ||
4054 | 52 | "http_host": "", | ||
4055 | 53 | "http_port": 0, | ||
4056 | 54 | "http_use_auth": "false", | ||
4057 | 55 | "https_host": "", | ||
4058 | 56 | "https_port": 0, | ||
4059 | 57 | "socks_host": "", | ||
4060 | 58 | "socks_port": 0, | ||
4061 | 59 | } | ||
4062 | 60 | |||
4063 | 61 | |||
4064 | 23 | class SaveHTTPChannel(http.HTTPChannel): | 62 | class SaveHTTPChannel(http.HTTPChannel): |
4065 | 24 | """A save protocol to be used in tests.""" | 63 | """A save protocol to be used in tests.""" |
4066 | 25 | 64 | ||
4067 | @@ -48,15 +87,26 @@ | |||
4068 | 48 | class BaseMockWebServer(object): | 87 | class BaseMockWebServer(object): |
4069 | 49 | """A mock webserver for testing""" | 88 | """A mock webserver for testing""" |
4070 | 50 | 89 | ||
4072 | 51 | def __init__(self): | 90 | def __init__(self, ssl_settings=None): |
4073 | 52 | """Start up this instance.""" | 91 | """Start up this instance.""" |
4074 | 53 | self.root = self.get_root_resource() | 92 | self.root = self.get_root_resource() |
4075 | 54 | self.site = SaveSite(self.root) | 93 | self.site = SaveSite(self.root) |
4076 | 55 | application = service.Application('web') | 94 | application = service.Application('web') |
4077 | 56 | self.service_collection = service.IServiceCollection(application) | 95 | self.service_collection = service.IServiceCollection(application) |
4078 | 57 | #pylint: disable=E1101 | 96 | #pylint: disable=E1101 |
4081 | 58 | self.tcpserver = internet.TCPServer(0, self.site) | 97 | ssl_context = None |
4082 | 59 | self.tcpserver.setServiceParent(self.service_collection) | 98 | if (ssl_settings is not None |
4083 | 99 | and 'key' in ssl_settings | ||
4084 | 100 | and 'cert' in ssl_settings): | ||
4085 | 101 | ssl_context = ssl.DefaultOpenSSLContextFactory(ssl_settings['key'], | ||
4086 | 102 | ssl_settings['cert']) | ||
4087 | 103 | self.ssl_server = internet.SSLServer(0, self.site, ssl_context) | ||
4088 | 104 | else: | ||
4089 | 105 | self.ssl_server = None | ||
4090 | 106 | self.server = internet.TCPServer(0, self.site) | ||
4091 | 107 | self.server.setServiceParent(self.service_collection) | ||
4092 | 108 | if self.ssl_server: | ||
4093 | 109 | self.ssl_server.setServiceParent(self.service_collection) | ||
4094 | 60 | self.service_collection.startService() | 110 | self.service_collection.startService() |
4095 | 61 | 111 | ||
4096 | 62 | def get_root_resource(self): | 112 | def get_root_resource(self): |
4097 | @@ -65,9 +115,27 @@ | |||
4098 | 65 | 115 | ||
4099 | 66 | def get_iri(self): | 116 | def get_iri(self): |
4100 | 67 | """Build the iri for this mock server.""" | 117 | """Build the iri for this mock server.""" |
4104 | 68 | #pylint: disable=W0212 | 118 | url = u"http://127.0.0.1:%d/" |
4105 | 69 | port_num = self.tcpserver._port.getHost().port | 119 | return url % self.get_port() |
4106 | 70 | return u"http://127.0.0.1:%d/" % port_num | 120 | |
4107 | 121 | def get_ssl_iri(self): | ||
4108 | 122 | """Build the ssl iri for this mock server.""" | ||
4109 | 123 | if self.ssl_server: | ||
4110 | 124 | url = u"https://127.0.0.1:%d/" | ||
4111 | 125 | return url % self.get_ssl_port() | ||
4112 | 126 | |||
4113 | 127 | def get_port(self): | ||
4114 | 128 | """Return the port where we are listening.""" | ||
4115 | 129 | # pylint: disable=W0212 | ||
4116 | 130 | return self.server._port.getHost().port | ||
4117 | 131 | # pylint: enable=W0212 | ||
4118 | 132 | |||
4119 | 133 | def get_ssl_port(self): | ||
4120 | 134 | """Return the ssl port where we are listening.""" | ||
4121 | 135 | # pylint: disable=W0212 | ||
4122 | 136 | if self.ssl_server: | ||
4123 | 137 | return self.ssl_server._port.getHost().port | ||
4124 | 138 | # pylint: enable=W0212 | ||
4125 | 71 | 139 | ||
4126 | 72 | def stop(self): | 140 | def stop(self): |
4127 | 73 | """Shut it down.""" | 141 | """Shut it down.""" |
4128 | 74 | 142 | ||
4129 | === modified file 'ubuntu_sso/utils/webclient/tests/test_gsettings.py' | |||
4130 | --- ubuntu_sso/utils/webclient/tests/test_gsettings.py 2012-02-10 19:20:33 +0000 | |||
4131 | +++ ubuntu_sso/utils/webclient/tests/test_gsettings.py 2012-03-20 16:10:09 +0000 | |||
4132 | @@ -18,43 +18,10 @@ | |||
4133 | 18 | from twisted.trial.unittest import TestCase | 18 | from twisted.trial.unittest import TestCase |
4134 | 19 | 19 | ||
4135 | 20 | from ubuntu_sso.utils.webclient import gsettings | 20 | from ubuntu_sso.utils.webclient import gsettings |
4173 | 21 | 21 | from ubuntu_sso.utils.webclient.tests import ( | |
4174 | 22 | # Some settings are not used as described in: | 22 | BASE_GSETTINGS_VALUES, |
4175 | 23 | # https://bugzilla.gnome.org/show_bug.cgi?id=648237 | 23 | TEMPLATE_GSETTINGS_OUTPUT, |
4176 | 24 | 24 | ) | |
4140 | 25 | TEMPLATE_GSETTINGS_OUTPUT = """\ | ||
4141 | 26 | org.gnome.system.proxy autoconfig-url '{autoconfig_url}' | ||
4142 | 27 | org.gnome.system.proxy ignore-hosts {ignore_hosts:s} | ||
4143 | 28 | org.gnome.system.proxy mode '{mode}' | ||
4144 | 29 | org.gnome.system.proxy.ftp host '{ftp_host}' | ||
4145 | 30 | org.gnome.system.proxy.ftp port {ftp_port} | ||
4146 | 31 | org.gnome.system.proxy.http authentication-password '{auth_password}' | ||
4147 | 32 | org.gnome.system.proxy.http authentication-user '{auth_user}' | ||
4148 | 33 | org.gnome.system.proxy.http host '{http_host}' | ||
4149 | 34 | org.gnome.system.proxy.http port {http_port} | ||
4150 | 35 | org.gnome.system.proxy.http use-authentication {http_use_auth} | ||
4151 | 36 | org.gnome.system.proxy.https host '{https_host}' | ||
4152 | 37 | org.gnome.system.proxy.https port {https_port} | ||
4153 | 38 | org.gnome.system.proxy.socks host '{socks_host}' | ||
4154 | 39 | org.gnome.system.proxy.socks port {socks_port} | ||
4155 | 40 | """ | ||
4156 | 41 | |||
4157 | 42 | BASE_GSETTINGS_VALUES = { | ||
4158 | 43 | "autoconfig_url": "", | ||
4159 | 44 | "ignore_hosts": ["localhost", "127.0.0.0/8"], | ||
4160 | 45 | "mode": "none", | ||
4161 | 46 | "ftp_host": "", | ||
4162 | 47 | "ftp_port": 0, | ||
4163 | 48 | "auth_password": "", | ||
4164 | 49 | "auth_user": "", | ||
4165 | 50 | "http_host": "", | ||
4166 | 51 | "http_port": 0, | ||
4167 | 52 | "http_use_auth": "false", | ||
4168 | 53 | "https_host": "", | ||
4169 | 54 | "https_port": 0, | ||
4170 | 55 | "socks_host": "", | ||
4171 | 56 | "socks_port": 0, | ||
4172 | 57 | } | ||
4177 | 58 | 25 | ||
4178 | 59 | 26 | ||
4179 | 60 | class ProxySettingsTestCase(TestCase): | 27 | class ProxySettingsTestCase(TestCase): |
4180 | @@ -83,25 +50,33 @@ | |||
4181 | 83 | ps = gsettings.get_proxy_settings() | 50 | ps = gsettings.get_proxy_settings() |
4182 | 84 | self.assertEqual(ps, expected) | 51 | self.assertEqual(ps, expected) |
4183 | 85 | 52 | ||
4184 | 53 | def _assert_parser_anonymous(self, scheme): | ||
4185 | 54 | """Assert the parsing of anonymous settings.""" | ||
4186 | 55 | template_values = dict(BASE_GSETTINGS_VALUES) | ||
4187 | 56 | expected_host = "expected_host" | ||
4188 | 57 | expected_port = 54321 | ||
4189 | 58 | expected = { | ||
4190 | 59 | "host": expected_host, | ||
4191 | 60 | "port": expected_port, | ||
4192 | 61 | } | ||
4193 | 62 | template_values.update({ | ||
4194 | 63 | "mode": "manual", | ||
4195 | 64 | scheme + "_host": expected_host, | ||
4196 | 65 | scheme + "_port": expected_port, | ||
4197 | 66 | }) | ||
4198 | 67 | fake_output = TEMPLATE_GSETTINGS_OUTPUT.format(**template_values) | ||
4199 | 68 | self.patch(gsettings.subprocess, "check_output", | ||
4200 | 69 | lambda _: fake_output) | ||
4201 | 70 | ps = gsettings.get_proxy_settings() | ||
4202 | 71 | self.assertEqual(ps[scheme], expected) | ||
4203 | 72 | |||
4204 | 86 | def test_gsettings_parser_http_anonymous(self): | 73 | def test_gsettings_parser_http_anonymous(self): |
4205 | 87 | """Test a parser of gsettings.""" | 74 | """Test a parser of gsettings.""" |
4223 | 88 | template_values = dict(BASE_GSETTINGS_VALUES) | 75 | self._assert_parser_anonymous('http') |
4224 | 89 | expected_host = "expected_host" | 76 | |
4225 | 90 | expected_port = 54321 | 77 | def test_gsettings_parser_https_anonymus(self): |
4226 | 91 | expected = { | 78 | """Test a parser of gsettings.""" |
4227 | 92 | "host": expected_host, | 79 | self._assert_parser_anonymous('https') |
4211 | 93 | "port": expected_port, | ||
4212 | 94 | } | ||
4213 | 95 | template_values.update({ | ||
4214 | 96 | "mode": "manual", | ||
4215 | 97 | "http_host": expected_host, | ||
4216 | 98 | "http_port": expected_port, | ||
4217 | 99 | }) | ||
4218 | 100 | fake_output = TEMPLATE_GSETTINGS_OUTPUT.format(**template_values) | ||
4219 | 101 | self.patch(gsettings.subprocess, "check_output", | ||
4220 | 102 | lambda _: fake_output) | ||
4221 | 103 | ps = gsettings.get_proxy_settings() | ||
4222 | 104 | self.assertEqual(ps, expected) | ||
4228 | 105 | 80 | ||
4229 | 106 | def test_gsettings_parser_http_authenticated(self): | 81 | def test_gsettings_parser_http_authenticated(self): |
4230 | 107 | """Test a parser of gsettings.""" | 82 | """Test a parser of gsettings.""" |
4231 | @@ -128,9 +103,9 @@ | |||
4232 | 128 | self.patch(gsettings.subprocess, "check_output", | 103 | self.patch(gsettings.subprocess, "check_output", |
4233 | 129 | lambda _: fake_output) | 104 | lambda _: fake_output) |
4234 | 130 | ps = gsettings.get_proxy_settings() | 105 | ps = gsettings.get_proxy_settings() |
4236 | 131 | self.assertEqual(ps, expected) | 106 | self.assertEqual(ps["http"], expected) |
4237 | 132 | 107 | ||
4239 | 133 | def test_gsettings_parser_authenticated_url(self): | 108 | def _assert_parser_authenticated_url(self, scheme): |
4240 | 134 | """Test a parser of gsettings with creds in the url.""" | 109 | """Test a parser of gsettings with creds in the url.""" |
4241 | 135 | template_values = dict(BASE_GSETTINGS_VALUES) | 110 | template_values = dict(BASE_GSETTINGS_VALUES) |
4242 | 136 | expected_host = "expected_host" | 111 | expected_host = "expected_host" |
4243 | @@ -147,15 +122,23 @@ | |||
4244 | 147 | } | 122 | } |
4245 | 148 | template_values.update({ | 123 | template_values.update({ |
4246 | 149 | "mode": "manual", | 124 | "mode": "manual", |
4249 | 150 | "http_host": composed_url, | 125 | scheme + "_host": composed_url, |
4250 | 151 | "http_port": expected_port, | 126 | scheme + "_port": expected_port, |
4251 | 152 | "http_use_auth": "false", | 127 | "http_use_auth": "false", |
4252 | 153 | }) | 128 | }) |
4253 | 154 | fake_output = TEMPLATE_GSETTINGS_OUTPUT.format(**template_values) | 129 | fake_output = TEMPLATE_GSETTINGS_OUTPUT.format(**template_values) |
4254 | 155 | self.patch(gsettings.subprocess, "check_output", | 130 | self.patch(gsettings.subprocess, "check_output", |
4255 | 156 | lambda _: fake_output) | 131 | lambda _: fake_output) |
4256 | 157 | ps = gsettings.get_proxy_settings() | 132 | ps = gsettings.get_proxy_settings() |
4258 | 158 | self.assertEqual(ps, expected) | 133 | self.assertEqual(ps[scheme], expected) |
4259 | 134 | |||
4260 | 135 | def test_gsettings_parser_http_authenticated_url(self): | ||
4261 | 136 | """Test a parser of gsettings with creds in the url.""" | ||
4262 | 137 | self._assert_parser_authenticated_url('http') | ||
4263 | 138 | |||
4264 | 139 | def test_gsettings_parser_https_authenticated_url(self): | ||
4265 | 140 | """Test a parser of gsettings with creds in the url.""" | ||
4266 | 141 | self._assert_parser_authenticated_url('https') | ||
4267 | 159 | 142 | ||
4268 | 160 | def test_gsettings_auth_over_url(self): | 143 | def test_gsettings_auth_over_url(self): |
4269 | 161 | """Test that the settings are more important that the url.""" | 144 | """Test that the settings are more important that the url.""" |
4270 | @@ -166,7 +149,7 @@ | |||
4271 | 166 | expected_password = "very secret password" | 149 | expected_password = "very secret password" |
4272 | 167 | composed_url = '%s:%s@%s' % ('user', 'random', | 150 | composed_url = '%s:%s@%s' % ('user', 'random', |
4273 | 168 | expected_host) | 151 | expected_host) |
4275 | 169 | expected = { | 152 | http_expected = { |
4276 | 170 | "host": expected_host, | 153 | "host": expected_host, |
4277 | 171 | "port": expected_port, | 154 | "port": expected_port, |
4278 | 172 | "username": expected_user, | 155 | "username": expected_user, |
4279 | @@ -184,7 +167,7 @@ | |||
4280 | 184 | self.patch(gsettings.subprocess, "check_output", | 167 | self.patch(gsettings.subprocess, "check_output", |
4281 | 185 | lambda _: fake_output) | 168 | lambda _: fake_output) |
4282 | 186 | ps = gsettings.get_proxy_settings() | 169 | ps = gsettings.get_proxy_settings() |
4284 | 187 | self.assertEqual(ps, expected) | 170 | self.assertEqual(ps["http"], http_expected) |
4285 | 188 | 171 | ||
4286 | 189 | 172 | ||
4287 | 190 | class ParseProxyHostTestCase(TestCase): | 173 | class ParseProxyHostTestCase(TestCase): |
4288 | 191 | 174 | ||
4289 | === modified file 'ubuntu_sso/utils/webclient/tests/test_webclient.py' | |||
4290 | --- ubuntu_sso/utils/webclient/tests/test_webclient.py 2012-02-13 13:14:18 +0000 | |||
4291 | +++ ubuntu_sso/utils/webclient/tests/test_webclient.py 2012-03-20 16:10:09 +0000 | |||
4292 | @@ -16,9 +16,12 @@ | |||
4293 | 16 | """Integration tests for the proxy-enabled webclient.""" | 16 | """Integration tests for the proxy-enabled webclient.""" |
4294 | 17 | 17 | ||
4295 | 18 | import os | 18 | import os |
4296 | 19 | import shutil | ||
4297 | 19 | import sys | 20 | import sys |
4298 | 20 | import urllib | 21 | import urllib |
4299 | 21 | 22 | ||
4300 | 23 | from OpenSSL import crypto | ||
4301 | 24 | from socket import gethostname | ||
4302 | 22 | from twisted.cred import checkers, portal | 25 | from twisted.cred import checkers, portal |
4303 | 23 | from twisted.internet import defer | 26 | from twisted.internet import defer |
4304 | 24 | from twisted.web import guard, http, resource | 27 | from twisted.web import guard, http, resource |
4305 | @@ -27,7 +30,15 @@ | |||
4306 | 27 | from ubuntuone.devtools.testcases import TestCase | 30 | from ubuntuone.devtools.testcases import TestCase |
4307 | 28 | from ubuntuone.devtools.testcases.squid import SquidTestCase | 31 | from ubuntuone.devtools.testcases.squid import SquidTestCase |
4308 | 29 | 32 | ||
4309 | 33 | from ubuntu_sso import ( | ||
4310 | 34 | keyring, | ||
4311 | 35 | EXCEPTION_RAISED, | ||
4312 | 36 | USER_SUCCESS, | ||
4313 | 37 | USER_CANCELLATION, | ||
4314 | 38 | ) | ||
4315 | 30 | from ubuntu_sso.utils import webclient | 39 | from ubuntu_sso.utils import webclient |
4316 | 40 | from ubuntu_sso.utils.ui import SSL_DETAILS_TEMPLATE | ||
4317 | 41 | from ubuntu_sso.utils.webclient import gsettings, txweb | ||
4318 | 31 | from ubuntu_sso.utils.webclient.common import BaseWebClient, HeaderDict, oauth | 42 | from ubuntu_sso.utils.webclient.common import BaseWebClient, HeaderDict, oauth |
4319 | 32 | from ubuntu_sso.utils.webclient.tests import BaseMockWebServer | 43 | from ubuntu_sso.utils.webclient.tests import BaseMockWebServer |
4320 | 33 | 44 | ||
4321 | @@ -188,9 +199,13 @@ | |||
4322 | 188 | return root | 199 | return root |
4323 | 189 | 200 | ||
4324 | 190 | 201 | ||
4328 | 191 | class FakeReactor(object): | 202 | class FakeQApplication(object): |
4329 | 192 | """A fake reactor object.""" | 203 | """A fake Qt module.""" |
4330 | 193 | qApp = "Sample qapp" | 204 | |
4331 | 205 | @classmethod | ||
4332 | 206 | def instance(cls): | ||
4333 | 207 | """Return the instance.""" | ||
4334 | 208 | return cls | ||
4335 | 194 | 209 | ||
4336 | 195 | 210 | ||
4337 | 196 | class ModuleSelectionTestCase(TestCase): | 211 | class ModuleSelectionTestCase(TestCase): |
4338 | @@ -201,10 +216,11 @@ | |||
4339 | 201 | self.patch(sys, "modules", {}) | 216 | self.patch(sys, "modules", {}) |
4340 | 202 | self.assertFalse(webclient.is_qt4reactor_installed()) | 217 | self.assertFalse(webclient.is_qt4reactor_installed()) |
4341 | 203 | 218 | ||
4343 | 204 | def test_is_qt4reactor_installed_installed(self): | 219 | def test_is_qt4reactor_installed_installed_core(self): |
4344 | 205 | """When the qt4reactor is installed, it returns true.""" | 220 | """When the qt4reactor is installed, it returns true.""" |
4347 | 206 | fake_sysmodules = {"twisted.internet.reactor": FakeReactor()} | 221 | from PyQt4 import QtCore |
4348 | 207 | self.patch(sys, "modules", fake_sysmodules) | 222 | |
4349 | 223 | self.patch(QtCore, 'QCoreApplication', FakeQApplication) | ||
4350 | 208 | self.assertTrue(webclient.is_qt4reactor_installed()) | 224 | self.assertTrue(webclient.is_qt4reactor_installed()) |
4351 | 209 | 225 | ||
4352 | 210 | def assert_module_name(self, module, expected_name): | 226 | def assert_module_name(self, module, expected_name): |
4353 | @@ -230,6 +246,7 @@ | |||
4354 | 230 | """Test for the webclient.""" | 246 | """Test for the webclient.""" |
4355 | 231 | 247 | ||
4356 | 232 | timeout = 1 | 248 | timeout = 1 |
4357 | 249 | webclient_factory = webclient.webclient_factory | ||
4358 | 233 | 250 | ||
4359 | 234 | @defer.inlineCallbacks | 251 | @defer.inlineCallbacks |
4360 | 235 | def setUp(self): | 252 | def setUp(self): |
4361 | @@ -237,7 +254,7 @@ | |||
4362 | 237 | self.ws = MockWebServer() | 254 | self.ws = MockWebServer() |
4363 | 238 | self.addCleanup(self.ws.stop) | 255 | self.addCleanup(self.ws.stop) |
4364 | 239 | self.base_iri = self.ws.get_iri() | 256 | self.base_iri = self.ws.get_iri() |
4366 | 240 | self.wc = webclient.webclient_factory() | 257 | self.wc = self.webclient_factory() |
4367 | 241 | self.addCleanup(self.wc.shutdown) | 258 | self.addCleanup(self.wc.shutdown) |
4368 | 242 | 259 | ||
4369 | 243 | @defer.inlineCallbacks | 260 | @defer.inlineCallbacks |
4370 | @@ -306,13 +323,22 @@ | |||
4371 | 306 | @defer.inlineCallbacks | 323 | @defer.inlineCallbacks |
4372 | 307 | def test_send_basic_auth(self): | 324 | def test_send_basic_auth(self): |
4373 | 308 | """The basic authentication headers are sent.""" | 325 | """The basic authentication headers are sent.""" |
4376 | 309 | other_wc = webclient.webclient_factory(username=SAMPLE_USERNAME, | 326 | other_wc = self.webclient_factory(username=SAMPLE_USERNAME, |
4377 | 310 | password=SAMPLE_PASSWORD) | 327 | password=SAMPLE_PASSWORD) |
4378 | 311 | self.addCleanup(other_wc.shutdown) | 328 | self.addCleanup(other_wc.shutdown) |
4379 | 312 | result = yield other_wc.request(self.base_iri + GUARDED) | 329 | result = yield other_wc.request(self.base_iri + GUARDED) |
4380 | 313 | self.assertEqual(SAMPLE_RESOURCE, result.content) | 330 | self.assertEqual(SAMPLE_RESOURCE, result.content) |
4381 | 314 | 331 | ||
4382 | 315 | @defer.inlineCallbacks | 332 | @defer.inlineCallbacks |
4383 | 333 | def test_send_basic_auth_wrong_credentials(self): | ||
4384 | 334 | """Wrong credentials returns a webclient error.""" | ||
4385 | 335 | other_wc = self.webclient_factory(username=SAMPLE_USERNAME, | ||
4386 | 336 | password="wrong password!") | ||
4387 | 337 | self.addCleanup(other_wc.shutdown) | ||
4388 | 338 | yield self.assertFailure(other_wc.request(self.base_iri + GUARDED), | ||
4389 | 339 | webclient.UnauthorizedError) | ||
4390 | 340 | |||
4391 | 341 | @defer.inlineCallbacks | ||
4392 | 316 | def test_request_is_oauth_signed(self): | 342 | def test_request_is_oauth_signed(self): |
4393 | 317 | """The request is oauth signed.""" | 343 | """The request is oauth signed.""" |
4394 | 318 | tsc = self.wc.get_timestamp_checker() | 344 | tsc = self.wc.get_timestamp_checker() |
4395 | @@ -348,6 +374,57 @@ | |||
4396 | 348 | "The type of %r must be bytes" % result.content) | 374 | "The type of %r must be bytes" % result.content) |
4397 | 349 | 375 | ||
4398 | 350 | 376 | ||
4399 | 377 | class FakeSavingReactor(object): | ||
4400 | 378 | """A fake reactor that saves connection attempts.""" | ||
4401 | 379 | |||
4402 | 380 | def __init__(self): | ||
4403 | 381 | """Initialize this fake instance.""" | ||
4404 | 382 | self.connections = [] | ||
4405 | 383 | |||
4406 | 384 | def connectTCP(self, host, port, factory, *args): | ||
4407 | 385 | """Fake the connection.""" | ||
4408 | 386 | self.connections.append((host, port, args)) | ||
4409 | 387 | factory.response_headers = {} | ||
4410 | 388 | factory.deferred = defer.succeed("response content") | ||
4411 | 389 | |||
4412 | 390 | def connectSSL(self, host, port, factory, *args): | ||
4413 | 391 | """Fake the connection.""" | ||
4414 | 392 | self.connections.append((host, port, args)) | ||
4415 | 393 | factory.response_headers = {} | ||
4416 | 394 | factory.deferred = defer.succeed("response content") | ||
4417 | 395 | |||
4418 | 396 | |||
4419 | 397 | class TxWebClientTestCase(WebClientTestCase): | ||
4420 | 398 | """Test case for txweb.""" | ||
4421 | 399 | |||
4422 | 400 | webclient_factory = txweb.WebClient | ||
4423 | 401 | |||
4424 | 402 | |||
4425 | 403 | class TxWebClientReactorReplaceableTestCase(TestCase): | ||
4426 | 404 | """In the txweb client the reactor is replaceable.""" | ||
4427 | 405 | |||
4428 | 406 | timeout = 3 | ||
4429 | 407 | FAKE_HOST = u"fake" | ||
4430 | 408 | FAKE_IRI_TEMPLATE = u"%%s://%s/fake_page" % FAKE_HOST | ||
4431 | 409 | |||
4432 | 410 | @defer.inlineCallbacks | ||
4433 | 411 | def _test_replaceable_reactor(self, iri): | ||
4434 | 412 | """The reactor can be replaced with the tunnel client.""" | ||
4435 | 413 | fake_reactor = FakeSavingReactor() | ||
4436 | 414 | wc = txweb.WebClient(fake_reactor) | ||
4437 | 415 | _response = yield wc.request(iri) | ||
4438 | 416 | host, _port, _args = fake_reactor.connections[0] | ||
4439 | 417 | self.assertEqual(host, self.FAKE_HOST) | ||
4440 | 418 | |||
4441 | 419 | def test_replaceable_reactor_http(self): | ||
4442 | 420 | """Test the replaceable reactor with an http iri.""" | ||
4443 | 421 | return self._test_replaceable_reactor(self.FAKE_IRI_TEMPLATE % "http") | ||
4444 | 422 | |||
4445 | 423 | def test_replaceable_reactor_https(self): | ||
4446 | 424 | """Test the replaceable reactor with an https iri.""" | ||
4447 | 425 | return self._test_replaceable_reactor(self.FAKE_IRI_TEMPLATE % "https") | ||
4448 | 426 | |||
4449 | 427 | |||
4450 | 351 | class TimestampCheckerTestCase(TestCase): | 428 | class TimestampCheckerTestCase(TestCase): |
4451 | 352 | """Tests for the timestampchecker classmethod.""" | 429 | """Tests for the timestampchecker classmethod.""" |
4452 | 353 | 430 | ||
4453 | @@ -375,6 +452,8 @@ | |||
4454 | 375 | class BasicProxyTestCase(SquidTestCase): | 452 | class BasicProxyTestCase(SquidTestCase): |
4455 | 376 | """Test that the proxy works at all.""" | 453 | """Test that the proxy works at all.""" |
4456 | 377 | 454 | ||
4457 | 455 | timeout = 3 | ||
4458 | 456 | |||
4459 | 378 | @defer.inlineCallbacks | 457 | @defer.inlineCallbacks |
4460 | 379 | def setUp(self): | 458 | def setUp(self): |
4461 | 380 | yield super(BasicProxyTestCase, self).setUp() | 459 | yield super(BasicProxyTestCase, self).setUp() |
4462 | @@ -404,10 +483,119 @@ | |||
4463 | 404 | result = yield self.wc.request(self.base_iri + SIMPLERESOURCE) | 483 | result = yield self.wc.request(self.base_iri + SIMPLERESOURCE) |
4464 | 405 | self.assert_header_contains(result.headers["Via"], "squid") | 484 | self.assert_header_contains(result.headers["Via"], "squid") |
4465 | 406 | 485 | ||
4466 | 486 | @defer.inlineCallbacks | ||
4467 | 487 | def test_auth_proxy_is_used_creds_requested(self): | ||
4468 | 488 | """The authenticated proxy is used by the webclient.""" | ||
4469 | 489 | settings = self.get_auth_proxy_settings() | ||
4470 | 490 | partial_settings = dict(host=settings['host'], port=settings['port']) | ||
4471 | 491 | |||
4472 | 492 | def fake_creds_request(domain, retry): | ||
4473 | 493 | """Fake user interaction.""" | ||
4474 | 494 | self.wc.proxy_username = settings['username'] | ||
4475 | 495 | self.wc.proxy_password = settings['password'] | ||
4476 | 496 | return defer.succeed(True) | ||
4477 | 497 | |||
4478 | 498 | self.patch(self.wc, 'request_proxy_auth_credentials', | ||
4479 | 499 | fake_creds_request) | ||
4480 | 500 | |||
4481 | 501 | self.wc.force_use_proxy(partial_settings) | ||
4482 | 502 | result = yield self.wc.request(self.base_iri + SIMPLERESOURCE) | ||
4483 | 503 | self.assert_header_contains(result.headers["Via"], "squid") | ||
4484 | 504 | |||
4485 | 505 | @defer.inlineCallbacks | ||
4486 | 506 | def test_auth_proxy_is_requested_creds_bad_details(self): | ||
4487 | 507 | """Test using wrong credentials with the proxy.""" | ||
4488 | 508 | settings = self.get_auth_proxy_settings() | ||
4489 | 509 | wrong_settings = dict(host=settings['host'], port=settings['port'], | ||
4490 | 510 | username=settings['password'], | ||
4491 | 511 | password=settings['username']) | ||
4492 | 512 | |||
4493 | 513 | def fake_creds_request(domain, retry): | ||
4494 | 514 | """Fake user interaction.""" | ||
4495 | 515 | self.wc.proxy_username = settings['username'] | ||
4496 | 516 | self.wc.proxy_password = settings['password'] | ||
4497 | 517 | return defer.succeed(True) | ||
4498 | 518 | |||
4499 | 519 | self.patch(self.wc, 'request_proxy_auth_credentials', | ||
4500 | 520 | fake_creds_request) | ||
4501 | 521 | |||
4502 | 522 | self.wc.force_use_proxy(wrong_settings) | ||
4503 | 523 | result = yield self.wc.request(self.base_iri + SIMPLERESOURCE) | ||
4504 | 524 | self.assert_header_contains(result.headers["Via"], "squid") | ||
4505 | 525 | |||
4506 | 526 | @defer.inlineCallbacks | ||
4507 | 527 | def test_auth_proxy_is_requested_creds_bad_details_user(self): | ||
4508 | 528 | """Test using no creds and user providing the wrong ones.""" | ||
4509 | 529 | settings = self.get_auth_proxy_settings() | ||
4510 | 530 | partial_settings = dict(host=settings['host'], port=settings['port']) | ||
4511 | 531 | |||
4512 | 532 | def fake_creds_request(domain, retry): | ||
4513 | 533 | """Fake user interaction.""" | ||
4514 | 534 | if retry: | ||
4515 | 535 | self.wc.proxy_username = settings['username'] | ||
4516 | 536 | self.wc.proxy_password = settings['password'] | ||
4517 | 537 | else: | ||
4518 | 538 | self.wc.proxy_username = settings['password'] | ||
4519 | 539 | self.wc.proxy_password = settings['username'] | ||
4520 | 540 | return defer.succeed(True) | ||
4521 | 541 | |||
4522 | 542 | self.patch(self.wc, 'request_proxy_auth_credentials', | ||
4523 | 543 | fake_creds_request) | ||
4524 | 544 | |||
4525 | 545 | self.wc.force_use_proxy(partial_settings) | ||
4526 | 546 | result = yield self.wc.request(self.base_iri + SIMPLERESOURCE) | ||
4527 | 547 | self.assert_header_contains(result.headers["Via"], "squid") | ||
4528 | 548 | |||
4529 | 549 | @defer.inlineCallbacks | ||
4530 | 550 | def test_auth_proxy_is_requested_creds_bad_details_everywhere(self): | ||
4531 | 551 | """Test when we pass the wrong settings and get the wrong settings.""" | ||
4532 | 552 | settings = self.get_auth_proxy_settings() | ||
4533 | 553 | wrong_settings = dict(host=settings['host'], port=settings['port'], | ||
4534 | 554 | username=settings['password'], | ||
4535 | 555 | password=settings['username']) | ||
4536 | 556 | |||
4537 | 557 | def fake_creds_request(domain, retry): | ||
4538 | 558 | """Fake user interaction.""" | ||
4539 | 559 | if retry: | ||
4540 | 560 | self.wc.proxy_username = settings['username'] | ||
4541 | 561 | self.wc.proxy_password = settings['password'] | ||
4542 | 562 | else: | ||
4543 | 563 | self.wc.proxy_username = settings['password'] | ||
4544 | 564 | self.wc.proxy_password = settings['username'] | ||
4545 | 565 | return defer.succeed(True) | ||
4546 | 566 | |||
4547 | 567 | self.patch(self.wc, 'request_proxy_auth_credentials', | ||
4548 | 568 | fake_creds_request) | ||
4549 | 569 | |||
4550 | 570 | self.wc.force_use_proxy(wrong_settings) | ||
4551 | 571 | result = yield self.wc.request(self.base_iri + SIMPLERESOURCE) | ||
4552 | 572 | self.assert_header_contains(result.headers["Via"], "squid") | ||
4553 | 573 | |||
4554 | 574 | def test_auth_proxy_is_requested_user_cancels(self): | ||
4555 | 575 | """Test when the user cancels the creds dialog.""" | ||
4556 | 576 | settings = self.get_auth_proxy_settings() | ||
4557 | 577 | partial_settings = dict(host=settings['host'], port=settings['port']) | ||
4558 | 578 | |||
4559 | 579 | def fake_creds_request(domain, retry): | ||
4560 | 580 | """Fake user interaction.""" | ||
4561 | 581 | return defer.succeed(False) | ||
4562 | 582 | |||
4563 | 583 | self.patch(self.wc, 'request_proxy_auth_credentials', | ||
4564 | 584 | fake_creds_request) | ||
4565 | 585 | |||
4566 | 586 | self.wc.force_use_proxy(partial_settings) | ||
4567 | 587 | self.failUnlessFailure(self.wc.request(self.base_iri + SIMPLERESOURCE), | ||
4568 | 588 | webclient.WebClientError) | ||
4569 | 589 | |||
4570 | 407 | if WEBCLIENT_MODULE_NAME.endswith(".txweb"): | 590 | if WEBCLIENT_MODULE_NAME.endswith(".txweb"): |
4571 | 408 | reason = "txweb does not support proxies." | 591 | reason = "txweb does not support proxies." |
4572 | 409 | test_anonymous_proxy_is_used.skip = reason | 592 | test_anonymous_proxy_is_used.skip = reason |
4574 | 410 | test_authenticated_proxy_is_used.skip = reason | 593 | test_authenticated_proxy_is_used.kip = reason |
4575 | 594 | test_auth_proxy_is_used_creds_requested.skip = reason | ||
4576 | 595 | test_auth_proxy_is_requested_creds_bad_details.skip = reason | ||
4577 | 596 | test_auth_proxy_is_requested_creds_bad_details_user.skip = reason | ||
4578 | 597 | test_auth_proxy_is_requested_creds_bad_details_everywhere.skip = reason | ||
4579 | 598 | test_auth_proxy_is_requested_user_cancels.skip = reason | ||
4580 | 411 | 599 | ||
4581 | 412 | 600 | ||
4582 | 413 | class HeaderDictTestCase(TestCase): | 601 | class HeaderDictTestCase(TestCase): |
4583 | @@ -562,3 +750,315 @@ | |||
4584 | 562 | """Test for the oauth signing code using HMAC-SHA1.""" | 750 | """Test for the oauth signing code using HMAC-SHA1.""" |
4585 | 563 | 751 | ||
4586 | 564 | oauth_sign = "HMAC-SHA1" | 752 | oauth_sign = "HMAC-SHA1" |
4587 | 753 | |||
4588 | 754 | |||
4589 | 755 | class FakeKeyring(object): | ||
4590 | 756 | """A fake keyring.""" | ||
4591 | 757 | |||
4592 | 758 | def __init__(self, creds): | ||
4593 | 759 | """A fake keyring.""" | ||
4594 | 760 | self.creds = creds | ||
4595 | 761 | |||
4596 | 762 | def __call__(self): | ||
4597 | 763 | """Fake instance callable.""" | ||
4598 | 764 | return self | ||
4599 | 765 | |||
4600 | 766 | def get_credentials(self, domain): | ||
4601 | 767 | """A fake get_credentials.""" | ||
4602 | 768 | if isinstance(self.creds, Exception): | ||
4603 | 769 | return defer.fail(self.creds) | ||
4604 | 770 | return defer.succeed(self.creds) | ||
4605 | 771 | |||
4606 | 772 | |||
4607 | 773 | class RequestProxyAuthTestCase(TestCase): | ||
4608 | 774 | """Test the spawn of the creds dialog.""" | ||
4609 | 775 | |||
4610 | 776 | @defer.inlineCallbacks | ||
4611 | 777 | def setUp(self): | ||
4612 | 778 | """Set the different tests.""" | ||
4613 | 779 | yield super(RequestProxyAuthTestCase, self).setUp() | ||
4614 | 780 | self.wc = webclient.webclient_factory() | ||
4615 | 781 | self.addCleanup(self.wc.shutdown) | ||
4616 | 782 | self.domain = 'domain' | ||
4617 | 783 | self.retry = False | ||
4618 | 784 | self.creds = dict(username='username', password='password') | ||
4619 | 785 | |||
4620 | 786 | self.keyring = FakeKeyring(self.creds) | ||
4621 | 787 | self.patch(keyring, 'Keyring', self.keyring) | ||
4622 | 788 | |||
4623 | 789 | self.spawn_return_code = USER_SUCCESS | ||
4624 | 790 | |||
4625 | 791 | def fake_spawn_process(args): | ||
4626 | 792 | """Fake spawning a process.""" | ||
4627 | 793 | if isinstance(self.spawn_return_code, Exception): | ||
4628 | 794 | return defer.fail(self.spawn_return_code) | ||
4629 | 795 | return defer.succeed(self.spawn_return_code) | ||
4630 | 796 | |||
4631 | 797 | self.patch(webclient.common, 'spawn_program', fake_spawn_process) | ||
4632 | 798 | |||
4633 | 799 | def test_spawn_error(self): | ||
4634 | 800 | """Test the case when we cannot spawn the process.""" | ||
4635 | 801 | self.spawn_return_code = Exception() | ||
4636 | 802 | self.failUnlessFailure(self.wc.request_proxy_auth_credentials( | ||
4637 | 803 | self.domain, True), | ||
4638 | 804 | webclient.WebClientError) | ||
4639 | 805 | |||
4640 | 806 | @defer.inlineCallbacks | ||
4641 | 807 | def test_creds_acquired(self): | ||
4642 | 808 | """Test the case in which we do get the creds.""" | ||
4643 | 809 | got_creds = yield self.wc.request_proxy_auth_credentials(self.domain, | ||
4644 | 810 | self.retry) | ||
4645 | 811 | self.assertTrue(got_creds, 'Return true when creds are present.') | ||
4646 | 812 | self.assertEqual(self.wc.proxy_username, self.creds['username']) | ||
4647 | 813 | self.assertEqual(self.wc.proxy_password, self.creds['password']) | ||
4648 | 814 | |||
4649 | 815 | def test_creds_acquired_keyring_error(self): | ||
4650 | 816 | """Test the case in which we cannot access the keyring.""" | ||
4651 | 817 | self.keyring.creds = Exception() | ||
4652 | 818 | self.failUnlessFailure(self.wc.request_proxy_auth_credentials( | ||
4653 | 819 | self.domain, self.retry), | ||
4654 | 820 | webclient.WebClientError) | ||
4655 | 821 | |||
4656 | 822 | @defer.inlineCallbacks | ||
4657 | 823 | def test_creds_none(self): | ||
4658 | 824 | """Test the case in which we got None from the keyring.""" | ||
4659 | 825 | self.keyring.creds = None | ||
4660 | 826 | got_creds = yield self.wc.request_proxy_auth_credentials(self.domain, | ||
4661 | 827 | self.retry) | ||
4662 | 828 | self.assertFalse(got_creds, 'Return false when creds are not present.') | ||
4663 | 829 | |||
4664 | 830 | def test_user_cancelation(self): | ||
4665 | 831 | """Test the case in which the user cancels.""" | ||
4666 | 832 | self.spawn_return_code = USER_CANCELLATION | ||
4667 | 833 | got_creds = yield self.wc.request_proxy_auth_credentials(self.domain, | ||
4668 | 834 | self.retry) | ||
4669 | 835 | self.assertFalse(got_creds, 'Return true when user cancels.') | ||
4670 | 836 | |||
4671 | 837 | def test_exception_error(self): | ||
4672 | 838 | """Test the case in which something bad happened.""" | ||
4673 | 839 | self.spawn_return_code = EXCEPTION_RAISED | ||
4674 | 840 | got_creds = yield self.wc.request_proxy_auth_credentials(self.domain, | ||
4675 | 841 | self.retry) | ||
4676 | 842 | self.assertFalse(got_creds, 'Return true when user cancels.') | ||
4677 | 843 | |||
4678 | 844 | |||
4679 | 845 | class BaseSSLTestCase(SquidTestCase): | ||
4680 | 846 | """Base test that allows to use ssl connections.""" | ||
4681 | 847 | |||
4682 | 848 | @defer.inlineCallbacks | ||
4683 | 849 | def setUp(self): | ||
4684 | 850 | """Set the diff tests.""" | ||
4685 | 851 | yield super(BaseSSLTestCase, self).setUp() | ||
4686 | 852 | self.cert_dir = os.path.join(self.tmpdir, 'cert') | ||
4687 | 853 | self.cert_details = dict(organization='Canonical', | ||
4688 | 854 | common_name=gethostname(), | ||
4689 | 855 | locality_name='London', | ||
4690 | 856 | unit='Ubuntu One', | ||
4691 | 857 | country_name='UK', | ||
4692 | 858 | state_name='London',) | ||
4693 | 859 | self.ssl_settings = self._generate_self_signed_certificate( | ||
4694 | 860 | self.cert_dir, | ||
4695 | 861 | self.cert_details) | ||
4696 | 862 | self.addCleanup(self._clean_ssl_certificate_files) | ||
4697 | 863 | |||
4698 | 864 | self.ws = MockWebServer(self.ssl_settings) | ||
4699 | 865 | self.addCleanup(self.ws.stop) | ||
4700 | 866 | self.base_iri = self.ws.get_iri() | ||
4701 | 867 | self.base_ssl_iri = self.ws.get_ssl_iri() | ||
4702 | 868 | |||
4703 | 869 | def _clean_ssl_certificate_files(self): | ||
4704 | 870 | """Remove the certificate files.""" | ||
4705 | 871 | if os.path.exists(self.cert_dir): | ||
4706 | 872 | shutil.rmtree(self.cert_dir) | ||
4707 | 873 | |||
4708 | 874 | def _generate_self_signed_certificate(self, cert_dir, cert_details): | ||
4709 | 875 | """Generate the required SSL certificates.""" | ||
4710 | 876 | if not os.path.exists(cert_dir): | ||
4711 | 877 | os.makedirs(cert_dir) | ||
4712 | 878 | cert_path = os.path.join(cert_dir, 'cert.crt') | ||
4713 | 879 | key_path = os.path.join(cert_dir, 'cert.key') | ||
4714 | 880 | |||
4715 | 881 | if os.path.exists(cert_path): | ||
4716 | 882 | os.unlink(cert_path) | ||
4717 | 883 | if os.path.exists(key_path): | ||
4718 | 884 | os.unlink(key_path) | ||
4719 | 885 | |||
4720 | 886 | # create a key pair | ||
4721 | 887 | key = crypto.PKey() | ||
4722 | 888 | key.generate_key(crypto.TYPE_RSA, 1024) | ||
4723 | 889 | |||
4724 | 890 | # create a self-signed cert | ||
4725 | 891 | cert = crypto.X509() | ||
4726 | 892 | cert.get_subject().C = cert_details['country_name'] | ||
4727 | 893 | cert.get_subject().ST = cert_details['state_name'] | ||
4728 | 894 | cert.get_subject().L = cert_details['locality_name'] | ||
4729 | 895 | cert.get_subject().O = cert_details['organization'] | ||
4730 | 896 | cert.get_subject().OU = cert_details['unit'] | ||
4731 | 897 | cert.get_subject().CN = cert_details['common_name'] | ||
4732 | 898 | cert.set_serial_number(1000) | ||
4733 | 899 | cert.gmtime_adj_notBefore(0) | ||
4734 | 900 | cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60) | ||
4735 | 901 | cert.set_issuer(cert.get_subject()) | ||
4736 | 902 | cert.set_pubkey(key) | ||
4737 | 903 | cert.sign(key, 'sha1') | ||
4738 | 904 | |||
4739 | 905 | with open(cert_path, 'wt') as fd: | ||
4740 | 906 | fd.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) | ||
4741 | 907 | |||
4742 | 908 | with open(key_path, 'wt') as fd: | ||
4743 | 909 | fd.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key)) | ||
4744 | 910 | |||
4745 | 911 | return dict(key=key_path, cert=cert_path) | ||
4746 | 912 | |||
4747 | 913 | |||
4748 | 914 | class CorrectProxyTestCase(BaseSSLTestCase): | ||
4749 | 915 | """Test the interaction with a SSL enabled proxy.""" | ||
4750 | 916 | |||
4751 | 917 | @defer.inlineCallbacks | ||
4752 | 918 | def setUp(self): | ||
4753 | 919 | """Set the tests.""" | ||
4754 | 920 | yield super(CorrectProxyTestCase, self).setUp() | ||
4755 | 921 | |||
4756 | 922 | # fake the gsettings to have diff settings for https and http | ||
4757 | 923 | http_settings = self.get_auth_proxy_settings() | ||
4758 | 924 | |||
4759 | 925 | #remember so that we can use them in the creds request | ||
4760 | 926 | proxy_username = http_settings['username'] | ||
4761 | 927 | proxy_password = http_settings['password'] | ||
4762 | 928 | |||
4763 | 929 | # delete the username and password so that we get a 407 for testing | ||
4764 | 930 | del http_settings['username'] | ||
4765 | 931 | del http_settings['password'] | ||
4766 | 932 | |||
4767 | 933 | https_settings = self.get_nonauth_proxy_settings() | ||
4768 | 934 | |||
4769 | 935 | proxy_settings = dict(http=http_settings, https=https_settings) | ||
4770 | 936 | self.patch(gsettings, "get_proxy_settings", lambda: proxy_settings) | ||
4771 | 937 | |||
4772 | 938 | self.wc = webclient.webclient_factory() | ||
4773 | 939 | self.addCleanup(self.wc.shutdown) | ||
4774 | 940 | |||
4775 | 941 | self.called = [] | ||
4776 | 942 | |||
4777 | 943 | def fake_creds_request(domain, retry): | ||
4778 | 944 | """Fake user interaction.""" | ||
4779 | 945 | self.called.append('request_proxy_auth_credentials') | ||
4780 | 946 | self.wc.proxy_username = proxy_username | ||
4781 | 947 | self.wc.proxy_password = proxy_password | ||
4782 | 948 | return defer.succeed(True) | ||
4783 | 949 | |||
4784 | 950 | self.patch(self.wc, 'request_proxy_auth_credentials', | ||
4785 | 951 | fake_creds_request) | ||
4786 | 952 | |||
4787 | 953 | def assert_header_contains(self, headers, expected): | ||
4788 | 954 | """One of the headers matching key must contain a given value.""" | ||
4789 | 955 | self.assertTrue(any(expected in value for value in headers)) | ||
4790 | 956 | |||
4791 | 957 | @defer.inlineCallbacks | ||
4792 | 958 | def test_https_request(self): | ||
4793 | 959 | """Test using the correct proxy for the ssl request. | ||
4794 | 960 | |||
4795 | 961 | In order to assert that the correct proxy is used we expect not to call | ||
4796 | 962 | the auth dialog since we set the https proxy not to use the auth proxy | ||
4797 | 963 | and to fail because we are reaching a https page with bad self-signed | ||
4798 | 964 | certs. | ||
4799 | 965 | """ | ||
4800 | 966 | # we fail due to the fake ssl cert | ||
4801 | 967 | yield self.failUnlessFailure(self.wc.request( | ||
4802 | 968 | self.base_ssl_iri + SIMPLERESOURCE), | ||
4803 | 969 | webclient.WebClientError) | ||
4804 | 970 | # https requests do not use the auth proxy therefore called should be | ||
4805 | 971 | # empty. This asserts that we are using the correct settings for the | ||
4806 | 972 | # request. | ||
4807 | 973 | self.assertEqual([], self.called) | ||
4808 | 974 | |||
4809 | 975 | @defer.inlineCallbacks | ||
4810 | 976 | def test_http_request(self): | ||
4811 | 977 | """Test using the correct proxy for the plain request. | ||
4812 | 978 | |||
4813 | 979 | This tests does the opposite to the https tests. We did set the auth | ||
4814 | 980 | proxy for the http request therefore we expect the proxy dialog to be | ||
4815 | 981 | used and not to get an error since we are not visiting a https with bad | ||
4816 | 982 | self-signed certs. | ||
4817 | 983 | """ | ||
4818 | 984 | # we do not fail since we are not going to the https page | ||
4819 | 985 | result = yield self.wc.request(self.base_iri + SIMPLERESOURCE) | ||
4820 | 986 | self.assert_header_contains(result.headers["Via"], "squid") | ||
4821 | 987 | # assert that we did go through the auth proxy | ||
4822 | 988 | self.assertIn('request_proxy_auth_credentials', self.called) | ||
4823 | 989 | |||
4824 | 990 | if WEBCLIENT_MODULE_NAME.endswith(".txweb"): | ||
4825 | 991 | reason = 'Multiple proxy settings is not supported.' | ||
4826 | 992 | test_https_request.skip = reason | ||
4827 | 993 | test_http_request.skip = reason | ||
4828 | 994 | |||
4829 | 995 | if WEBCLIENT_MODULE_NAME.endswith(".libsoup"): | ||
4830 | 996 | reason = 'Hard to test since we need to fully mock gsettings.' | ||
4831 | 997 | test_https_request.skip = reason | ||
4832 | 998 | test_http_request.skip = reason | ||
4833 | 999 | |||
4834 | 1000 | if WEBCLIENT_MODULE_NAME.endswith(".qtnetwork"): | ||
4835 | 1001 | reason = ('Updating proxy settings is not well support due to bug' | ||
4836 | 1002 | ' QTBUG-14850: https://bugreports.qt-project.org/' | ||
4837 | 1003 | 'browse/QTBUG-14850') | ||
4838 | 1004 | test_https_request.skip = reason | ||
4839 | 1005 | test_http_request.skip = reason | ||
4840 | 1006 | |||
4841 | 1007 | |||
4842 | 1008 | class SSLTestCase(BaseSSLTestCase): | ||
4843 | 1009 | """Test error handling when dealing with ssl.""" | ||
4844 | 1010 | |||
4845 | 1011 | @defer.inlineCallbacks | ||
4846 | 1012 | def setUp(self): | ||
4847 | 1013 | """Set the diff tests.""" | ||
4848 | 1014 | yield super(SSLTestCase, self).setUp() | ||
4849 | 1015 | |||
4850 | 1016 | self.wc = webclient.webclient_factory() | ||
4851 | 1017 | self.addCleanup(self.wc.shutdown) | ||
4852 | 1018 | |||
4853 | 1019 | self.return_code = USER_CANCELLATION | ||
4854 | 1020 | self.called = [] | ||
4855 | 1021 | |||
4856 | 1022 | def fake_launch_ssl_dialog(client, domain, details): | ||
4857 | 1023 | """Fake the ssl dialog.""" | ||
4858 | 1024 | self.called.append(('_launch_ssl_dialog', domain, details)) | ||
4859 | 1025 | return defer.succeed(self.return_code) | ||
4860 | 1026 | |||
4861 | 1027 | self.patch(BaseWebClient, '_launch_ssl_dialog', fake_launch_ssl_dialog) | ||
4862 | 1028 | |||
4863 | 1029 | @defer.inlineCallbacks | ||
4864 | 1030 | def _assert_ssl_fail_user_accepts(self, proxy_settings=None): | ||
4865 | 1031 | """Assert the dialog is shown in an ssl fail.""" | ||
4866 | 1032 | self.return_code = USER_SUCCESS | ||
4867 | 1033 | if proxy_settings: | ||
4868 | 1034 | self.wc.force_use_proxy(proxy_settings) | ||
4869 | 1035 | yield self.wc.request(self.base_ssl_iri + SIMPLERESOURCE) | ||
4870 | 1036 | details = SSL_DETAILS_TEMPLATE % self.cert_details | ||
4871 | 1037 | self.assertIn(('_launch_ssl_dialog', gethostname(), details), | ||
4872 | 1038 | self.called) | ||
4873 | 1039 | |||
4874 | 1040 | def test_ssl_fail_dialog_user_accepts(self): | ||
4875 | 1041 | """Test showing the dialog and accepting.""" | ||
4876 | 1042 | self._assert_ssl_fail_user_accepts() | ||
4877 | 1043 | |||
4878 | 1044 | def test_ssl_fail_dialog_user_accepts_via_proxy(self): | ||
4879 | 1045 | """Test showing the dialog and accepting when using a proxy.""" | ||
4880 | 1046 | self._assert_ssl_fail_user_accepts(self.get_nonauth_proxy_settings()) | ||
4881 | 1047 | |||
4882 | 1048 | def test_ssl_fail_dialog_user_rejects(self): | ||
4883 | 1049 | """Test showing the dialog and rejecting.""" | ||
4884 | 1050 | self.failUnlessFailure(self.wc.request(self.base_iri + SIMPLERESOURCE), | ||
4885 | 1051 | webclient.WebClientError) | ||
4886 | 1052 | |||
4887 | 1053 | def test_format_ssl_details(self): | ||
4888 | 1054 | """Assert that details are correctly formatted""" | ||
4889 | 1055 | details = SSL_DETAILS_TEMPLATE % self.cert_details | ||
4890 | 1056 | self.assertEqual(details, | ||
4891 | 1057 | self.wc.format_ssl_details(self.cert_details)) | ||
4892 | 1058 | |||
4893 | 1059 | if (WEBCLIENT_MODULE_NAME.endswith(".txweb") or | ||
4894 | 1060 | WEBCLIENT_MODULE_NAME.endswith(".libsoup")): | ||
4895 | 1061 | reason = 'SSL support has not yet been implemented.' | ||
4896 | 1062 | test_ssl_fail_dialog_user_accepts.skip = reason | ||
4897 | 1063 | test_ssl_fail_dialog_user_accepts_via_proxy.skip = reason | ||
4898 | 1064 | test_ssl_fail_dialog_user_rejects.skip = reason | ||
4899 | 565 | 1065 | ||
4900 | === modified file 'ubuntu_sso/utils/webclient/txweb.py' | |||
4901 | --- ubuntu_sso/utils/webclient/txweb.py 2012-02-07 19:36:50 +0000 | |||
4902 | +++ ubuntu_sso/utils/webclient/txweb.py 2012-03-20 16:10:09 +0000 | |||
4903 | @@ -16,11 +16,9 @@ | |||
4904 | 16 | """A webclient backend that uses twisted.web.client.""" | 16 | """A webclient backend that uses twisted.web.client.""" |
4905 | 17 | 17 | ||
4906 | 18 | import base64 | 18 | import base64 |
4912 | 19 | 19 | import urlparse | |
4913 | 20 | from StringIO import StringIO | 20 | |
4914 | 21 | 21 | from twisted.internet import defer | |
4910 | 22 | from twisted.internet import defer, protocol | ||
4911 | 23 | from zope.interface import implements | ||
4915 | 24 | 22 | ||
4916 | 25 | from ubuntu_sso.utils.webclient.common import ( | 23 | from ubuntu_sso.utils.webclient.common import ( |
4917 | 26 | BaseWebClient, | 24 | BaseWebClient, |
4918 | @@ -31,64 +29,80 @@ | |||
4919 | 31 | ) | 29 | ) |
4920 | 32 | 30 | ||
4921 | 33 | 31 | ||
4964 | 34 | class StringProtocol(protocol.Protocol): | 32 | class RawResponse(object): |
4965 | 35 | """Hold the stuff received in a StringIO.""" | 33 | """A raw response from the webcall.""" |
4966 | 36 | 34 | ||
4967 | 37 | # pylint: disable=C0103 | 35 | def __init__(self, headers, content, code=200, phrase="OK"): |
4968 | 38 | def __init__(self): | 36 | """Initialize this response.""" |
4969 | 39 | """Initialize this instance.""" | 37 | self.headers = headers |
4970 | 40 | self.deferred = defer.Deferred() | 38 | self.content = content |
4971 | 41 | self.content = StringIO() | 39 | self.code = code |
4972 | 42 | 40 | self.phrase = phrase | |
4931 | 43 | def dataReceived(self, data): | ||
4932 | 44 | """Some more blocks received.""" | ||
4933 | 45 | self.content.write(data) | ||
4934 | 46 | |||
4935 | 47 | def connectionLost(self, reason=protocol.connectionDone): | ||
4936 | 48 | """No more bytes available.""" | ||
4937 | 49 | self.deferred.callback(self.content.getvalue()) | ||
4938 | 50 | |||
4939 | 51 | |||
4940 | 52 | class StringProducer(object): | ||
4941 | 53 | """Simple implementation of IBodyProducer.""" | ||
4942 | 54 | |||
4943 | 55 | # delay import, otherwise a default reactor gets installed | ||
4944 | 56 | from twisted.web import iweb | ||
4945 | 57 | |||
4946 | 58 | implements(iweb.IBodyProducer) | ||
4947 | 59 | |||
4948 | 60 | def __init__(self, body): | ||
4949 | 61 | """Initialize this instance with some bytes.""" | ||
4950 | 62 | self.body = body | ||
4951 | 63 | self.length = len(body) | ||
4952 | 64 | |||
4953 | 65 | # pylint: disable=C0103 | ||
4954 | 66 | def startProducing(self, consumer): | ||
4955 | 67 | """Start producing to the given IConsumer provider.""" | ||
4956 | 68 | consumer.write(self.body) | ||
4957 | 69 | return defer.succeed(None) | ||
4958 | 70 | |||
4959 | 71 | def pauseProducing(self): | ||
4960 | 72 | """In our case, do nothing.""" | ||
4961 | 73 | |||
4962 | 74 | def stopProducing(self): | ||
4963 | 75 | """In our case, do nothing.""" | ||
4973 | 76 | 41 | ||
4974 | 77 | 42 | ||
4975 | 78 | class WebClient(BaseWebClient): | 43 | class WebClient(BaseWebClient): |
4976 | 79 | """A simple web client that does not support proxies, yet.""" | 44 | """A simple web client that does not support proxies, yet.""" |
4977 | 80 | 45 | ||
4984 | 81 | # delay import, otherwise a default reactor gets installed | 46 | def __init__(self, connector=None, context_factory=None, **kwargs): |
4985 | 82 | from twisted.internet import reactor | 47 | """Initialize this webclient.""" |
4986 | 83 | from twisted.web import client, http, http_headers | 48 | super(WebClient, self).__init__(**kwargs) |
4987 | 84 | 49 | ||
4988 | 85 | # Undefined variable 'http_headers', 'client', 'reactor', 'http' | 50 | if connector is None: |
4989 | 86 | # pylint: disable=E0602 | 51 | from twisted.internet import reactor |
4990 | 52 | self.connector = reactor | ||
4991 | 53 | else: | ||
4992 | 54 | self.connector = connector | ||
4993 | 55 | |||
4994 | 56 | if context_factory is None: | ||
4995 | 57 | from twisted.internet import ssl | ||
4996 | 58 | self.context_factory = ssl.ClientContextFactory() | ||
4997 | 59 | else: | ||
4998 | 60 | self.context_factory = context_factory | ||
4999 | 61 | |||
5000 | 62 | @defer.inlineCallbacks |