Merge lp:~dobey/ubuntu-sso-client/update-4-0 into lp:ubuntu-sso-client/stable-4-0

Proposed by dobey on 2012-07-18
Status: Merged
Approved by: dobey on 2012-07-19
Approved revision: no longer in the source branch.
Merged at revision: 962
Proposed branch: lp:~dobey/ubuntu-sso-client/update-4-0
Merge into: lp:ubuntu-sso-client/stable-4-0
Diff against target: 5938 lines (+230/-4813)
46 files modified
README (+6/-12)
bin/ubuntu-sso-login (+1/-1)
bin/ubuntu-sso-login-gtk (+0/-44)
bin/ubuntu-sso-login-qt (+1/-1)
bin/ubuntu-sso-proxy-creds-qt (+1/-1)
bin/ubuntu-sso-ssl-certificate-qt (+1/-1)
data/gtk/ui.glade (+0/-920)
po/POTFILES.in (+0/-1)
run-mac-tests (+2/-3)
run-tests (+1/-13)
run-tests.bat (+2/-2)
setup.py (+1/-3)
ubuntu_sso/__init__.py (+3/-7)
ubuntu_sso/credentials.py (+13/-19)
ubuntu_sso/gtk/__init__.py (+0/-29)
ubuntu_sso/gtk/gui.py (+0/-1152)
ubuntu_sso/gtk/main.py (+0/-41)
ubuntu_sso/gtk/tests/__init__.py (+0/-29)
ubuntu_sso/gtk/tests/test_gui.py (+0/-2291)
ubuntu_sso/gtk/tests/test_main.py (+0/-51)
ubuntu_sso/keyring/__init__.py (+9/-3)
ubuntu_sso/keyring/linux.py (+10/-5)
ubuntu_sso/main/__init__.py (+0/-2)
ubuntu_sso/main/darwin.py (+3/-17)
ubuntu_sso/main/glib.py (+9/-9)
ubuntu_sso/main/tests/test_windows.py (+0/-13)
ubuntu_sso/main/windows.py (+3/-11)
ubuntu_sso/networkstate/tests/run_nwmgr_standalone.py (+2/-1)
ubuntu_sso/qt/__init__.py (+0/-1)
ubuntu_sso/qt/loadingoverlay.py (+1/-1)
ubuntu_sso/qt/setup_account_page.py (+2/-2)
ubuntu_sso/qt/tests/show_gui.py (+0/-2)
ubuntu_sso/tests/__init__.py (+1/-1)
ubuntu_sso/tests/test_account.py (+2/-2)
ubuntu_sso/tests/test_credentials.py (+38/-57)
ubuntu_sso/utils/__init__.py (+40/-11)
ubuntu_sso/utils/tcpactivation.py (+1/-1)
ubuntu_sso/utils/tests/test_common.py (+1/-10)
ubuntu_sso/utils/tests/test_parse_args.py (+3/-2)
ubuntu_sso/utils/tests/test_tcpactivation.py (+7/-5)
ubuntu_sso/utils/webclient/common.py (+22/-16)
ubuntu_sso/utils/webclient/qtnetwork.py (+1/-1)
ubuntu_sso/utils/webclient/restful.py (+8/-2)
ubuntu_sso/utils/webclient/tests/test_restful.py (+11/-5)
ubuntu_sso/utils/webclient/tests/test_webclient.py (+14/-8)
ubuntu_sso/utils/webclient/txweb.py (+10/-4)
To merge this branch: bzr merge lp:~dobey/ubuntu-sso-client/update-4-0
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) trivial 2012-07-18 Approve on 2012-07-18
Review via email: mp+115606@code.launchpad.net

Commit Message

[Mike McCracken]

    - Use dirspec get_program_path for cross-platform executable path finding.
    - use buildout-generated python wrapper to run setup.py in run-mac-tests (LP: #1023043)

[Natalia Bidart]

    - Removing everything related to the GTK+ UI (LP: #1017690).

[Brian Curtin]

    Import the StringIO class in a Python 2 and 3 way
    Try Python 3 urllib names and fall back to Python 2 as needed.
    Use Python 3's print function regardless of version.
    Convert _winreg importing to use winreg on both Python 2 and 3
    Change xrange usage to range for Python 3.

[Rodney Dawes]

    Avoid using /usr/bin/env in scripts.

To post a comment you must log in.
Alejandro J. Cura (alecu) wrote :

looks just like trunk. +1

review: Approve (trivial)
Ubuntu One Auto Pilot (otto-pilot) wrote :
Download full text (15.1 KiB)

The attempt to merge lp:~dobey/ubuntu-sso-client/update-4-0 into lp:ubuntu-sso-client/stable-4-0 failed. Below is the output from the failed tests.

*** Running QT test suite for ubuntu_sso ***
running build
Compiled data/qt/reset_password.ui into ubuntu_sso/qt/ui/reset_password_ui.py
Compiled data/qt/proxy_credentials_dialog.ui into ubuntu_sso/qt/ui/proxy_credentials_dialog_ui.py
compiled data/qt/resources.qrc into ubuntu_sso/qt/ui/resources_rc.py
Compiled data/qt/loadingoverlay.ui into ubuntu_sso/qt/ui/loadingoverlay_ui.py
Compiled data/qt/ssl_dialog.ui into ubuntu_sso/qt/ui/ssl_dialog_ui.py
Compiled data/qt/success_message.ui into ubuntu_sso/qt/ui/success_message_ui.py
Compiled data/qt/setup_account.ui into ubuntu_sso/qt/ui/setup_account_ui.py
Compiled data/qt/current_user_sign_in.ui into ubuntu_sso/qt/ui/current_user_sign_in_ui.py
Compiled data/qt/error_message.ui into ubuntu_sso/qt/ui/error_message_ui.py
Compiled data/qt/forgotten_password.ui into ubuntu_sso/qt/ui/forgotten_password_ui.py
Compiled data/qt/network_detection.ui into ubuntu_sso/qt/ui/network_detection_ui.py
Compiled data/qt/email_verification.ui into ubuntu_sso/qt/ui/email_verification_ui.py
running build_py
creating build
creating build/lib.linux-x86_64-2.7
creating build/lib.linux-x86_64-2.7/ubuntu_sso
copying ubuntu_sso/credentials.py -> build/lib.linux-x86_64-2.7/ubuntu_sso
copying ubuntu_sso/account.py -> build/lib.linux-x86_64-2.7/ubuntu_sso
copying ubuntu_sso/__init__.py -> build/lib.linux-x86_64-2.7/ubuntu_sso
copying ubuntu_sso/logger.py -> build/lib.linux-x86_64-2.7/ubuntu_sso
creating build/lib.linux-x86_64-2.7/ubuntu_sso/tests
copying ubuntu_sso/tests/test_account.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/tests
copying ubuntu_sso/tests/linux.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/tests
copying ubuntu_sso/tests/test_credentials.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/tests
copying ubuntu_sso/tests/__init__.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/tests
creating build/lib.linux-x86_64-2.7/ubuntu_sso/keyring
copying ubuntu_sso/keyring/pykeyring.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/keyring
copying ubuntu_sso/keyring/linux.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/keyring
copying ubuntu_sso/keyring/__init__.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/keyring
creating build/lib.linux-x86_64-2.7/ubuntu_sso/keyring/tests
copying ubuntu_sso/keyring/tests/__init__.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/keyring/tests
copying ubuntu_sso/keyring/tests/test_pykeyring.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/keyring/tests
copying ubuntu_sso/keyring/tests/test_common.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/keyring/tests
copying ubuntu_sso/keyring/tests/test_linux.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/keyring/tests
creating build/lib.linux-x86_64-2.7/ubuntu_sso/main
copying ubuntu_sso/main/perspective_broker.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/main
copying ubuntu_sso/main/glib.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/main
copying ubuntu_sso/main/linux.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/main
copying ubuntu_sso/main/darwin.py -> build/lib.linux-x86_64-2.7/ubuntu_sso/main
copying ubuntu_sso/main/qt.py -> build/lib.linux-x...

962. By Brian Curtin on 2012-07-19

[Mike McCracken]

    - Use dirspec get_program_path for cross-platform executable path finding.
    - use buildout-generated python wrapper to run setup.py in run-mac-tests (LP: #1023043)

[Natalia Bidart]

    - Removing everything related to the GTK+ UI (LP: #1017690).

[Brian Curtin]

    Import the StringIO class in a Python 2 and 3 way
    Try Python 3 urllib names and fall back to Python 2 as needed.
    Use Python 3's print function regardless of version.
    Convert _winreg importing to use winreg on both Python 2 and 3
    Change xrange usage to range for Python 3.

[Rodney Dawes]

    Avoid using /usr/bin/env in scripts.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README'
2--- README 2010-12-09 20:00:20 +0000
3+++ README 2012-07-18 18:54:28 +0000
4@@ -108,21 +108,15 @@
5 required to be checked to continue the registration. Also, a link to the terms
6 will be offered for browsing. If not defined, nothing will be shown.
7
8- * 'window_id': an X11 window identifier to be used for
9- gtk.gdk.set_transient_for, so the dialog is always shown above the specified
10- parent window. Usually it is str(!GtkWidget.window.xid) for GTK callers. If
11- not defined, no parent will be set.
12+ * 'window_id': DEPRECATED - used to be an X11 window identifier to be used for
13+ gtk.gdk.set_transient_for, so the dialog was always shown above the specified
14+ parent window.
15
16 Additionally, this 'ui_settings' can provide 2 extra keys to define which UI
17 module and UI class should be used in case the service needs to open a
18 graphical interface to the end user. These keys are:
19
20- * 'ui_class': the name of a class that lives within 'ui_module' and that
21- accepts proper parameters (TODO: document parameters). An example of this
22- class can be seen at ubuntu_sso.gtk.gui.UbuntuSSOClientGUI.
23-
24- * 'ui_module': a string pointing to a python module that holds 'ui_class' in
25- it, and is importable from the system PYTHONPATH.
26+ * 'ui_executable': the name of an executable script to be used as GUI.
27
28 ==== login(String app_name, Dict of {String, String} ui_settings) ====
29
30@@ -192,8 +186,8 @@
31
32 * "detailed_error" is a string containing a Python stacktrace or something
33 similar to help the developers debug the problem. The application calling
34- ubuntu-sso-client may choose to show it to the user using a !GtkExpander, or
35- write it to a log file, etc.
36+ ubuntu-sso-client may choose to show it to the user using some sort of
37+ expander, or write it to a log file, etc.
38
39 ==== CredentialsCleared(String app_name) ====
40
41
42=== modified file 'bin/ubuntu-sso-login'
43--- bin/ubuntu-sso-login 2012-06-07 17:19:53 +0000
44+++ bin/ubuntu-sso-login 2012-07-18 18:54:28 +0000
45@@ -1,4 +1,4 @@
46-#!/usr/bin/env python
47+#!/usr/bin/python
48 # -*- coding: utf-8 -*-
49 #
50 # Copyright 2009-2012 Canonical Ltd.
51
52=== removed file 'bin/ubuntu-sso-login-gtk'
53--- bin/ubuntu-sso-login-gtk 2012-04-09 17:38:24 +0000
54+++ bin/ubuntu-sso-login-gtk 1970-01-01 00:00:00 +0000
55@@ -1,44 +0,0 @@
56-#!/usr/bin/env python
57-# -*- coding: utf-8 -*-
58-#
59-# Copyright 2012 Canonical Ltd.
60-#
61-# This program is free software: you can redistribute it and/or modify it
62-# under the terms of the GNU General Public License version 3, as published
63-# by the Free Software Foundation.
64-#
65-# This program is distributed in the hope that it will be useful, but
66-# WITHOUT ANY WARRANTY; without even the implied warranties of
67-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
68-# PURPOSE. See the GNU General Public License for more details.
69-#
70-# You should have received a copy of the GNU General Public License along
71-# with this program. If not, see <http://www.gnu.org/licenses/>.
72-#
73-# In addition, as a special exception, the copyright holders give
74-# permission to link the code of portions of this program with the
75-# OpenSSL library under certain conditions as described in each
76-# individual source file, and distribute linked combinations
77-# including the two.
78-# You must obey the GNU General Public License in all respects
79-# for all of the code used other than OpenSSL. If you modify
80-# file(s) with this exception, you may extend this exception to your
81-# version of the file(s), but you are not obligated to do so. If you
82-# do not wish to do so, delete this exception statement from your
83-# version. If you delete this exception statement from all source
84-# files in the program, then also delete it here.
85-"""Start the sso GTK UI."""
86-
87-# Invalid name "ubuntu-sso-login-gtk", pylint: disable=C0103
88-# Access to a protected member, pylint: disable=W0212
89-
90-from ubuntu_sso.gtk.main import main
91-from ubuntu_sso.utils.ui import parse_args
92-
93-from dbus.mainloop.glib import DBusGMainLoop
94-DBusGMainLoop(set_as_default=True)
95-
96-
97-if __name__ == "__main__":
98- args = parse_args()
99- main(**dict(args._get_kwargs()))
100
101=== modified file 'bin/ubuntu-sso-login-qt'
102--- bin/ubuntu-sso-login-qt 2012-05-01 12:38:09 +0000
103+++ bin/ubuntu-sso-login-qt 2012-07-18 18:54:28 +0000
104@@ -1,4 +1,4 @@
105-#!/usr/bin/env python
106+#!/usr/bin/python
107 # -*- coding: utf-8 -*-
108 #
109 # Copyright 2012 Canonical Ltd.
110
111=== modified file 'bin/ubuntu-sso-proxy-creds-qt'
112--- bin/ubuntu-sso-proxy-creds-qt 2012-04-09 17:38:24 +0000
113+++ bin/ubuntu-sso-proxy-creds-qt 2012-07-18 18:54:28 +0000
114@@ -1,4 +1,4 @@
115-#!/usr/bin/env python
116+#!/usr/bin/python
117 # -*- coding: utf-8 -*-
118 #
119 # Copyright 2012 Canonical Ltd.
120
121=== modified file 'bin/ubuntu-sso-ssl-certificate-qt'
122--- bin/ubuntu-sso-ssl-certificate-qt 2012-04-09 17:38:24 +0000
123+++ bin/ubuntu-sso-ssl-certificate-qt 2012-07-18 18:54:28 +0000
124@@ -1,4 +1,4 @@
125-#!/usr/bin/env python
126+#!/usr/bin/python
127 # -*- coding: utf-8 -*-
128 #
129 # Copyright 2012 Canonical Ltd.
130
131=== removed directory 'data/gtk'
132=== removed file 'data/gtk/ui.glade'
133--- data/gtk/ui.glade 2012-01-25 15:34:41 +0000
134+++ data/gtk/ui.glade 1970-01-01 00:00:00 +0000
135@@ -1,920 +0,0 @@
136-<?xml version="1.0" encoding="UTF-8"?>
137-<interface>
138- <requires lib="gtk+" version="2.16"/>
139- <object class="GtkVBox" id="enter_details_vbox">
140- <property name="visible">True</property>
141- <property name="can_focus">False</property>
142- <property name="spacing">5</property>
143- <child>
144- <object class="GtkHBox" id="emails_hbox">
145- <property name="visible">True</property>
146- <property name="can_focus">False</property>
147- <property name="spacing">5</property>
148- <property name="homogeneous">True</property>
149- <child>
150- <placeholder/>
151- </child>
152- <child>
153- <placeholder/>
154- </child>
155- </object>
156- <packing>
157- <property name="expand">False</property>
158- <property name="fill">True</property>
159- <property name="position">0</property>
160- </packing>
161- </child>
162- <child>
163- <object class="GtkHBox" id="passwords_hbox">
164- <property name="visible">True</property>
165- <property name="can_focus">False</property>
166- <property name="spacing">5</property>
167- <property name="homogeneous">True</property>
168- <child>
169- <placeholder/>
170- </child>
171- <child>
172- <placeholder/>
173- </child>
174- </object>
175- <packing>
176- <property name="expand">False</property>
177- <property name="fill">True</property>
178- <property name="position">1</property>
179- </packing>
180- </child>
181- <child>
182- <object class="GtkLabel" id="password_help_label">
183- <property name="visible">True</property>
184- <property name="can_focus">False</property>
185- <property name="label">password help</property>
186- <property name="wrap">True</property>
187- </object>
188- <packing>
189- <property name="expand">False</property>
190- <property name="fill">True</property>
191- <property name="position">2</property>
192- </packing>
193- </child>
194- <child>
195- <object class="GtkAlignment" id="alignment5">
196- <property name="visible">True</property>
197- <property name="can_focus">False</property>
198- <property name="xscale">0</property>
199- <property name="yscale">0</property>
200- <child>
201- <object class="GtkHBox" id="hbox1">
202- <property name="visible">True</property>
203- <property name="can_focus">False</property>
204- <child>
205- <object class="GtkVBox" id="captcha_vbox">
206- <property name="width_request">300</property>
207- <property name="height_request">60</property>
208- <property name="visible">True</property>
209- <property name="can_focus">False</property>
210- <child>
211- <object class="GtkEventBox" id="captcha_loading">
212- <property name="width_request">300</property>
213- <property name="height_request">60</property>
214- <property name="visible">True</property>
215- <property name="can_focus">False</property>
216- <child>
217- <placeholder/>
218- </child>
219- </object>
220- <packing>
221- <property name="expand">False</property>
222- <property name="fill">False</property>
223- <property name="position">0</property>
224- </packing>
225- </child>
226- <child>
227- <object class="GtkImage" id="captcha_image">
228- <property name="width_request">300</property>
229- <property name="visible">True</property>
230- <property name="can_focus">False</property>
231- <property name="stock">gtk-missing-image</property>
232- </object>
233- <packing>
234- <property name="expand">True</property>
235- <property name="fill">True</property>
236- <property name="position">1</property>
237- </packing>
238- </child>
239- </object>
240- <packing>
241- <property name="expand">False</property>
242- <property name="fill">False</property>
243- <property name="position">0</property>
244- </packing>
245- </child>
246- <child>
247- <object class="GtkVBox" id="vbox1">
248- <property name="visible">True</property>
249- <property name="can_focus">False</property>
250- <child>
251- <object class="GtkButton" id="captcha_reload_button">
252- <property name="use_action_appearance">False</property>
253- <property name="visible">True</property>
254- <property name="can_focus">True</property>
255- <property name="receives_default">True</property>
256- <property name="use_action_appearance">False</property>
257- <property name="relief">none</property>
258- <property name="focus_on_click">False</property>
259- <signal name="clicked" handler="on_captcha_reload_button_clicked" swapped="no"/>
260- <child>
261- <object class="GtkImage" id="image1">
262- <property name="visible">True</property>
263- <property name="can_focus">False</property>
264- <property name="icon_name">reload</property>
265- </object>
266- </child>
267- </object>
268- <packing>
269- <property name="expand">False</property>
270- <property name="fill">True</property>
271- <property name="position">0</property>
272- </packing>
273- </child>
274- <child>
275- <placeholder/>
276- </child>
277- <child>
278- <placeholder/>
279- </child>
280- </object>
281- <packing>
282- <property name="expand">False</property>
283- <property name="fill">True</property>
284- <property name="position">1</property>
285- </packing>
286- </child>
287- </object>
288- </child>
289- </object>
290- <packing>
291- <property name="expand">False</property>
292- <property name="fill">True</property>
293- <property name="position">3</property>
294- </packing>
295- </child>
296- <child>
297- <object class="GtkVBox" id="captcha_solution_vbox">
298- <property name="visible">True</property>
299- <property name="can_focus">False</property>
300- <child>
301- <placeholder/>
302- </child>
303- </object>
304- <packing>
305- <property name="expand">False</property>
306- <property name="fill">True</property>
307- <property name="position">4</property>
308- </packing>
309- </child>
310- <child>
311- <object class="GtkCheckButton" id="yes_to_updates_checkbutton">
312- <property name="label" translatable="yes">yes to updates</property>
313- <property name="use_action_appearance">False</property>
314- <property name="visible">True</property>
315- <property name="can_focus">True</property>
316- <property name="receives_default">False</property>
317- <property name="use_action_appearance">False</property>
318- <property name="active">True</property>
319- <property name="draw_indicator">True</property>
320- </object>
321- <packing>
322- <property name="expand">False</property>
323- <property name="fill">True</property>
324- <property name="position">5</property>
325- </packing>
326- </child>
327- <child>
328- <object class="GtkVBox" id="tc_vbox">
329- <property name="visible">True</property>
330- <property name="can_focus">False</property>
331- <property name="spacing">5</property>
332- <child>
333- <object class="GtkCheckButton" id="yes_to_tc_checkbutton">
334- <property name="label" translatable="yes">yes to tc</property>
335- <property name="use_action_appearance">False</property>
336- <property name="visible">True</property>
337- <property name="can_focus">True</property>
338- <property name="receives_default">False</property>
339- <property name="use_action_appearance">False</property>
340- <property name="draw_indicator">True</property>
341- </object>
342- <packing>
343- <property name="expand">False</property>
344- <property name="fill">True</property>
345- <property name="position">0</property>
346- </packing>
347- </child>
348- <child>
349- <object class="GtkHButtonBox" id="hbuttonbox3">
350- <property name="visible">True</property>
351- <property name="can_focus">False</property>
352- <property name="layout_style">start</property>
353- <child>
354- <object class="GtkButton" id="tc_button">
355- <property name="label">show tc</property>
356- <property name="use_action_appearance">False</property>
357- <property name="visible">True</property>
358- <property name="can_focus">True</property>
359- <property name="receives_default">True</property>
360- <property name="use_action_appearance">False</property>
361- <signal name="clicked" handler="on_tc_button_clicked" swapped="no"/>
362- </object>
363- <packing>
364- <property name="expand">False</property>
365- <property name="fill">False</property>
366- <property name="position">1</property>
367- </packing>
368- </child>
369- </object>
370- <packing>
371- <property name="expand">False</property>
372- <property name="fill">True</property>
373- <property name="position">1</property>
374- </packing>
375- </child>
376- <child>
377- <object class="GtkLabel" id="tc_warning_label">
378- <property name="visible">True</property>
379- <property name="can_focus">False</property>
380- <property name="xalign">0</property>
381- <property name="label">tc warning</property>
382- <property name="wrap">True</property>
383- </object>
384- <packing>
385- <property name="expand">True</property>
386- <property name="fill">True</property>
387- <property name="position">2</property>
388- </packing>
389- </child>
390- </object>
391- <packing>
392- <property name="expand">False</property>
393- <property name="fill">True</property>
394- <property name="position">6</property>
395- </packing>
396- </child>
397- <child>
398- <object class="GtkHBox" id="hbox2">
399- <property name="visible">True</property>
400- <property name="can_focus">False</property>
401- <property name="spacing">5</property>
402- <child>
403- <object class="GtkHButtonBox" id="hbuttonbox9">
404- <property name="visible">True</property>
405- <property name="can_focus">False</property>
406- <property name="layout_style">start</property>
407- <child>
408- <object class="GtkLinkButton" id="login_button">
409- <property name="label">login button</property>
410- <property name="use_action_appearance">False</property>
411- <property name="visible">True</property>
412- <property name="can_focus">True</property>
413- <property name="receives_default">True</property>
414- <property name="use_action_appearance">False</property>
415- <property name="relief">none</property>
416- <property name="uri">foo</property>
417- <signal name="activate-link" handler="on_activate_link" swapped="no"/>
418- <signal name="clicked" handler="on_sign_in_button_clicked" swapped="no"/>
419- </object>
420- <packing>
421- <property name="expand">False</property>
422- <property name="fill">False</property>
423- <property name="position">0</property>
424- </packing>
425- </child>
426- </object>
427- <packing>
428- <property name="expand">False</property>
429- <property name="fill">True</property>
430- <property name="position">0</property>
431- </packing>
432- </child>
433- <child>
434- <object class="GtkHButtonBox" id="hbuttonbox1">
435- <property name="visible">True</property>
436- <property name="can_focus">False</property>
437- <property name="spacing">5</property>
438- <property name="layout_style">end</property>
439- <child>
440- <object class="GtkButton" id="join_cancel_button">
441- <property name="label">gtk-cancel</property>
442- <property name="use_action_appearance">False</property>
443- <property name="visible">True</property>
444- <property name="can_focus">True</property>
445- <property name="receives_default">True</property>
446- <property name="use_action_appearance">False</property>
447- <property name="use_stock">True</property>
448- </object>
449- <packing>
450- <property name="expand">False</property>
451- <property name="fill">False</property>
452- <property name="position">0</property>
453- </packing>
454- </child>
455- <child>
456- <object class="GtkButton" id="join_ok_button">
457- <property name="label">gtk-go-forward</property>
458- <property name="use_action_appearance">False</property>
459- <property name="visible">True</property>
460- <property name="can_focus">True</property>
461- <property name="receives_default">True</property>
462- <property name="use_action_appearance">False</property>
463- <property name="use_stock">True</property>
464- <signal name="clicked" handler="on_join_ok_button_clicked" swapped="no"/>
465- </object>
466- <packing>
467- <property name="expand">False</property>
468- <property name="fill">False</property>
469- <property name="position">1</property>
470- </packing>
471- </child>
472- </object>
473- <packing>
474- <property name="expand">False</property>
475- <property name="fill">True</property>
476- <property name="pack_type">end</property>
477- <property name="position">1</property>
478- </packing>
479- </child>
480- </object>
481- <packing>
482- <property name="expand">False</property>
483- <property name="fill">True</property>
484- <property name="pack_type">end</property>
485- <property name="position">7</property>
486- </packing>
487- </child>
488- </object>
489- <object class="GtkVBox" id="finish_vbox">
490- <property name="visible">True</property>
491- <property name="can_focus">False</property>
492- <property name="spacing">10</property>
493- <child>
494- <object class="GtkLabel" id="finish_label">
495- <property name="visible">True</property>
496- <property name="can_focus">False</property>
497- <property name="wrap">True</property>
498- </object>
499- <packing>
500- <property name="expand">True</property>
501- <property name="fill">True</property>
502- <property name="position">0</property>
503- </packing>
504- </child>
505- <child>
506- <object class="GtkHButtonBox" id="hbuttonbox8">
507- <property name="visible">True</property>
508- <property name="can_focus">False</property>
509- <property name="layout_style">end</property>
510- <child>
511- <object class="GtkButton" id="finish_close_button">
512- <property name="label">gtk-close</property>
513- <property name="use_action_appearance">False</property>
514- <property name="visible">True</property>
515- <property name="can_focus">True</property>
516- <property name="receives_default">True</property>
517- <property name="use_action_appearance">False</property>
518- <property name="use_stock">True</property>
519- <signal name="clicked" handler="on_close_clicked" swapped="no"/>
520- </object>
521- <packing>
522- <property name="expand">False</property>
523- <property name="fill">False</property>
524- <property name="position">0</property>
525- </packing>
526- </child>
527- </object>
528- <packing>
529- <property name="expand">False</property>
530- <property name="fill">True</property>
531- <property name="position">1</property>
532- </packing>
533- </child>
534- </object>
535- <object class="GtkVBox" id="login_vbox">
536- <property name="visible">True</property>
537- <property name="can_focus">False</property>
538- <property name="spacing">10</property>
539- <child>
540- <object class="GtkAlignment" id="alignment3">
541- <property name="visible">True</property>
542- <property name="can_focus">False</property>
543- <property name="xscale">0</property>
544- <property name="yscale">0</property>
545- <child>
546- <object class="GtkVBox" id="login_details_vbox">
547- <property name="visible">True</property>
548- <property name="can_focus">False</property>
549- <property name="spacing">5</property>
550- <child>
551- <placeholder/>
552- </child>
553- <child>
554- <placeholder/>
555- </child>
556- </object>
557- </child>
558- </object>
559- <packing>
560- <property name="expand">True</property>
561- <property name="fill">True</property>
562- <property name="position">0</property>
563- </packing>
564- </child>
565- <child>
566- <object class="GtkHBox" id="hbox3">
567- <property name="visible">True</property>
568- <property name="can_focus">False</property>
569- <property name="spacing">5</property>
570- <child>
571- <object class="GtkHButtonBox" id="hbuttonbox10">
572- <property name="visible">True</property>
573- <property name="can_focus">False</property>
574- <property name="layout_style">start</property>
575- <child>
576- <object class="GtkLinkButton" id="forgotten_password_button">
577- <property name="label" translatable="yes">forgot password button</property>
578- <property name="use_action_appearance">False</property>
579- <property name="visible">True</property>
580- <property name="can_focus">True</property>
581- <property name="receives_default">True</property>
582- <property name="has_tooltip">True</property>
583- <property name="use_action_appearance">False</property>
584- <property name="relief">none</property>
585- <property name="uri">foo</property>
586- <signal name="activate-link" handler="on_activate_link" swapped="no"/>
587- <signal name="clicked" handler="on_forgotten_password_button_clicked" swapped="no"/>
588- </object>
589- <packing>
590- <property name="expand">False</property>
591- <property name="fill">False</property>
592- <property name="padding">10</property>
593- <property name="position">0</property>
594- </packing>
595- </child>
596- </object>
597- <packing>
598- <property name="expand">False</property>
599- <property name="fill">True</property>
600- <property name="position">0</property>
601- </packing>
602- </child>
603- <child>
604- <object class="GtkHButtonBox" id="hbuttonbox5">
605- <property name="visible">True</property>
606- <property name="can_focus">False</property>
607- <property name="spacing">5</property>
608- <property name="layout_style">end</property>
609- <child>
610- <object class="GtkButton" id="login_cancel_button">
611- <property name="label">gtk-cancel</property>
612- <property name="use_action_appearance">False</property>
613- <property name="visible">True</property>
614- <property name="can_focus">True</property>
615- <property name="receives_default">True</property>
616- <property name="use_action_appearance">False</property>
617- <property name="use_stock">True</property>
618- </object>
619- <packing>
620- <property name="expand">False</property>
621- <property name="fill">False</property>
622- <property name="position">0</property>
623- </packing>
624- </child>
625- <child>
626- <object class="GtkButton" id="login_back_button">
627- <property name="label">gtk-go-back</property>
628- <property name="use_action_appearance">False</property>
629- <property name="visible">True</property>
630- <property name="can_focus">True</property>
631- <property name="receives_default">True</property>
632- <property name="use_action_appearance">False</property>
633- <property name="use_stock">True</property>
634- <signal name="clicked" handler="on_login_back_button_clicked" swapped="no"/>
635- </object>
636- <packing>
637- <property name="expand">False</property>
638- <property name="fill">False</property>
639- <property name="position">1</property>
640- </packing>
641- </child>
642- <child>
643- <object class="GtkButton" id="login_ok_button">
644- <property name="label">gtk-connect</property>
645- <property name="use_action_appearance">False</property>
646- <property name="visible">True</property>
647- <property name="can_focus">True</property>
648- <property name="receives_default">True</property>
649- <property name="use_action_appearance">False</property>
650- <property name="use_stock">True</property>
651- <signal name="clicked" handler="on_login_connect_button_clicked" swapped="no"/>
652- </object>
653- <packing>
654- <property name="expand">False</property>
655- <property name="fill">False</property>
656- <property name="position">2</property>
657- </packing>
658- </child>
659- </object>
660- <packing>
661- <property name="expand">False</property>
662- <property name="fill">True</property>
663- <property name="pack_type">end</property>
664- <property name="position">1</property>
665- </packing>
666- </child>
667- </object>
668- <packing>
669- <property name="expand">False</property>
670- <property name="fill">True</property>
671- <property name="position">1</property>
672- </packing>
673- </child>
674- </object>
675- <object class="GtkVBox" id="processing_vbox">
676- <property name="visible">True</property>
677- <property name="can_focus">False</property>
678- <property name="spacing">10</property>
679- <child>
680- <placeholder/>
681- </child>
682- </object>
683- <object class="GtkVBox" id="request_password_token_vbox">
684- <property name="visible">True</property>
685- <property name="can_focus">False</property>
686- <property name="spacing">10</property>
687- <child>
688- <object class="GtkAlignment" id="alignment2">
689- <property name="visible">True</property>
690- <property name="can_focus">False</property>
691- <property name="xscale">0</property>
692- <property name="yscale">0</property>
693- <child>
694- <object class="GtkVBox" id="request_password_token_details_vbox">
695- <property name="visible">True</property>
696- <property name="can_focus">False</property>
697- <property name="spacing">5</property>
698- <child>
699- <placeholder/>
700- </child>
701- </object>
702- </child>
703- </object>
704- <packing>
705- <property name="expand">True</property>
706- <property name="fill">True</property>
707- <property name="position">0</property>
708- </packing>
709- </child>
710- <child>
711- <object class="GtkHButtonBox" id="hbuttonbox7">
712- <property name="visible">True</property>
713- <property name="can_focus">False</property>
714- <property name="spacing">5</property>
715- <property name="layout_style">end</property>
716- <child>
717- <object class="GtkButton" id="request_password_token_cancel_button">
718- <property name="label">gtk-cancel</property>
719- <property name="use_action_appearance">False</property>
720- <property name="visible">True</property>
721- <property name="can_focus">True</property>
722- <property name="receives_default">True</property>
723- <property name="use_action_appearance">False</property>
724- <property name="use_stock">True</property>
725- </object>
726- <packing>
727- <property name="expand">False</property>
728- <property name="fill">False</property>
729- <property name="position">0</property>
730- </packing>
731- </child>
732- <child>
733- <object class="GtkButton" id="request_password_token_back_button">
734- <property name="label">gtk-go-back</property>
735- <property name="use_action_appearance">False</property>
736- <property name="visible">True</property>
737- <property name="can_focus">True</property>
738- <property name="receives_default">True</property>
739- <property name="use_action_appearance">False</property>
740- <property name="use_stock">True</property>
741- <signal name="clicked" handler="on_request_password_token_back_button_clicked" swapped="no"/>
742- </object>
743- <packing>
744- <property name="expand">False</property>
745- <property name="fill">False</property>
746- <property name="position">1</property>
747- </packing>
748- </child>
749- <child>
750- <object class="GtkButton" id="request_password_token_ok_button">
751- <property name="label">gtk-ok</property>
752- <property name="use_action_appearance">False</property>
753- <property name="visible">True</property>
754- <property name="can_focus">True</property>
755- <property name="receives_default">True</property>
756- <property name="use_action_appearance">False</property>
757- <property name="use_stock">True</property>
758- <signal name="clicked" handler="on_request_password_token_ok_button_clicked" swapped="no"/>
759- </object>
760- <packing>
761- <property name="expand">False</property>
762- <property name="fill">False</property>
763- <property name="position">2</property>
764- </packing>
765- </child>
766- </object>
767- <packing>
768- <property name="expand">False</property>
769- <property name="fill">True</property>
770- <property name="position">1</property>
771- </packing>
772- </child>
773- </object>
774- <object class="GtkVBox" id="set_new_password_vbox">
775- <property name="visible">True</property>
776- <property name="can_focus">False</property>
777- <property name="spacing">10</property>
778- <child>
779- <object class="GtkVBox" id="vbox2">
780- <property name="visible">True</property>
781- <property name="can_focus">False</property>
782- <child>
783- <object class="GtkLabel" id="reset_password_help_label">
784- <property name="visible">True</property>
785- <property name="can_focus">False</property>
786- <property name="label">label</property>
787- <property name="wrap">True</property>
788- </object>
789- <packing>
790- <property name="expand">False</property>
791- <property name="fill">True</property>
792- <property name="position">0</property>
793- </packing>
794- </child>
795- <child>
796- <object class="GtkAlignment" id="alignment1">
797- <property name="visible">True</property>
798- <property name="can_focus">False</property>
799- <property name="xscale">0</property>
800- <property name="yscale">0</property>
801- <child>
802- <object class="GtkVBox" id="set_new_password_details_vbox">
803- <property name="visible">True</property>
804- <property name="can_focus">False</property>
805- <property name="spacing">5</property>
806- <child>
807- <placeholder/>
808- </child>
809- <child>
810- <placeholder/>
811- </child>
812- <child>
813- <placeholder/>
814- </child>
815- </object>
816- </child>
817- </object>
818- <packing>
819- <property name="expand">True</property>
820- <property name="fill">True</property>
821- <property name="position">1</property>
822- </packing>
823- </child>
824- </object>
825- <packing>
826- <property name="expand">True</property>
827- <property name="fill">True</property>
828- <property name="position">0</property>
829- </packing>
830- </child>
831- <child>
832- <object class="GtkHButtonBox" id="hbuttonbox6">
833- <property name="visible">True</property>
834- <property name="can_focus">False</property>
835- <property name="spacing">5</property>
836- <property name="layout_style">end</property>
837- <child>
838- <object class="GtkButton" id="set_new_password_cancel_button">
839- <property name="label">gtk-cancel</property>
840- <property name="use_action_appearance">False</property>
841- <property name="visible">True</property>
842- <property name="can_focus">True</property>
843- <property name="receives_default">True</property>
844- <property name="use_action_appearance">False</property>
845- <property name="use_stock">True</property>
846- </object>
847- <packing>
848- <property name="expand">False</property>
849- <property name="fill">False</property>
850- <property name="position">0</property>
851- </packing>
852- </child>
853- <child>
854- <object class="GtkButton" id="set_new_password_ok_button">
855- <property name="label">gtk-ok</property>
856- <property name="use_action_appearance">False</property>
857- <property name="visible">True</property>
858- <property name="can_focus">True</property>
859- <property name="receives_default">True</property>
860- <property name="use_action_appearance">False</property>
861- <property name="use_stock">True</property>
862- <signal name="clicked" handler="on_set_new_password_ok_button_clicked" swapped="no"/>
863- </object>
864- <packing>
865- <property name="expand">False</property>
866- <property name="fill">False</property>
867- <property name="position">1</property>
868- </packing>
869- </child>
870- </object>
871- <packing>
872- <property name="expand">False</property>
873- <property name="fill">True</property>
874- <property name="position">1</property>
875- </packing>
876- </child>
877- </object>
878- <object class="GtkVBox" id="tc_browser_vbox">
879- <property name="visible">True</property>
880- <property name="can_focus">False</property>
881- <signal name="hide" handler="on_tc_browser_vbox_hide" swapped="no"/>
882- <child>
883- <object class="GtkScrolledWindow" id="tc_browser_window">
884- <property name="visible">True</property>
885- <property name="can_focus">True</property>
886- <property name="border_width">10</property>
887- <property name="hscrollbar_policy">never</property>
888- <property name="shadow_type">in</property>
889- <child>
890- <placeholder/>
891- </child>
892- </object>
893- <packing>
894- <property name="expand">True</property>
895- <property name="fill">True</property>
896- <property name="position">0</property>
897- </packing>
898- </child>
899- <child>
900- <object class="GtkHButtonBox" id="hbuttonbox4">
901- <property name="visible">True</property>
902- <property name="can_focus">False</property>
903- <property name="layout_style">end</property>
904- <child>
905- <object class="GtkButton" id="tc_back_button">
906- <property name="label">gtk-go-back</property>
907- <property name="use_action_appearance">False</property>
908- <property name="visible">True</property>
909- <property name="can_focus">True</property>
910- <property name="receives_default">True</property>
911- <property name="use_action_appearance">False</property>
912- <property name="use_stock">True</property>
913- <signal name="clicked" handler="on_tc_back_button_clicked" swapped="no"/>
914- </object>
915- <packing>
916- <property name="expand">False</property>
917- <property name="fill">False</property>
918- <property name="position">0</property>
919- </packing>
920- </child>
921- </object>
922- <packing>
923- <property name="expand">False</property>
924- <property name="fill">True</property>
925- <property name="position">1</property>
926- </packing>
927- </child>
928- </object>
929- <object class="GtkVBox" id="verify_email_vbox">
930- <property name="visible">True</property>
931- <property name="can_focus">False</property>
932- <property name="spacing">10</property>
933- <child>
934- <object class="GtkAlignment" id="alignment4">
935- <property name="visible">True</property>
936- <property name="can_focus">False</property>
937- <property name="xscale">0</property>
938- <property name="yscale">0</property>
939- <child>
940- <object class="GtkVBox" id="verify_email_details_vbox">
941- <property name="visible">True</property>
942- <property name="can_focus">False</property>
943- <child>
944- <placeholder/>
945- </child>
946- </object>
947- </child>
948- </object>
949- <packing>
950- <property name="expand">True</property>
951- <property name="fill">True</property>
952- <property name="position">0</property>
953- </packing>
954- </child>
955- <child>
956- <object class="GtkHButtonBox" id="hbuttonbox2">
957- <property name="visible">True</property>
958- <property name="can_focus">False</property>
959- <property name="spacing">5</property>
960- <property name="layout_style">end</property>
961- <child>
962- <object class="GtkButton" id="verify_token_button">
963- <property name="label">gtk-ok</property>
964- <property name="use_action_appearance">False</property>
965- <property name="visible">True</property>
966- <property name="can_focus">True</property>
967- <property name="receives_default">True</property>
968- <property name="use_action_appearance">False</property>
969- <property name="use_stock">True</property>
970- <signal name="clicked" handler="on_verify_token_button_clicked" swapped="no"/>
971- </object>
972- <packing>
973- <property name="expand">False</property>
974- <property name="fill">False</property>
975- <property name="position">0</property>
976- </packing>
977- </child>
978- </object>
979- <packing>
980- <property name="expand">False</property>
981- <property name="fill">True</property>
982- <property name="position">1</property>
983- </packing>
984- </child>
985- </object>
986- <object class="GtkWindow" id="window">
987- <property name="can_focus">False</property>
988- <property name="border_width">10</property>
989- <property name="window_position">center</property>
990- <signal name="delete-event" handler="on_close_clicked" swapped="no"/>
991- <child>
992- <object class="GtkVBox" id="window_vbox">
993- <property name="visible">True</property>
994- <property name="can_focus">False</property>
995- <property name="spacing">5</property>
996- <child>
997- <object class="GtkLabel" id="header_label">
998- <property name="visible">True</property>
999- <property name="can_focus">False</property>
1000- <property name="xalign">0</property>
1001- <property name="label" translatable="yes">Header Label </property>
1002- <property name="wrap">True</property>
1003- </object>
1004- <packing>
1005- <property name="expand">False</property>
1006- <property name="fill">True</property>
1007- <property name="padding">5</property>
1008- <property name="position">0</property>
1009- </packing>
1010- </child>
1011- <child>
1012- <object class="GtkLabel" id="help_label">
1013- <property name="visible">True</property>
1014- <property name="can_focus">False</property>
1015- <property name="xalign">0</property>
1016- <property name="label" translatable="yes">help label</property>
1017- <property name="wrap">True</property>
1018- </object>
1019- <packing>
1020- <property name="expand">False</property>
1021- <property name="fill">True</property>
1022- <property name="position">1</property>
1023- </packing>
1024- </child>
1025- <child>
1026- <object class="GtkLabel" id="warning_label">
1027- <property name="visible">True</property>
1028- <property name="can_focus">False</property>
1029- <property name="xalign">0</property>
1030- <property name="label" translatable="yes">warning label</property>
1031- <property name="wrap">True</property>
1032- </object>
1033- <packing>
1034- <property name="expand">False</property>
1035- <property name="fill">True</property>
1036- <property name="position">2</property>
1037- </packing>
1038- </child>
1039- <child>
1040- <object class="GtkNotebook" id="content">
1041- <property name="visible">True</property>
1042- <property name="can_focus">True</property>
1043- <property name="show_tabs">False</property>
1044- <property name="show_border">False</property>
1045- </object>
1046- <packing>
1047- <property name="expand">True</property>
1048- <property name="fill">True</property>
1049- <property name="position">3</property>
1050- </packing>
1051- </child>
1052- </object>
1053- </child>
1054- </object>
1055-</interface>
1056
1057=== modified file 'po/POTFILES.in'
1058--- po/POTFILES.in 2012-03-16 15:14:28 +0000
1059+++ po/POTFILES.in 2012-07-18 18:54:28 +0000
1060@@ -1,2 +1,1 @@
1061-ubuntu_sso/gtk/gui.py
1062 ubuntu_sso/utils/ui.py
1063
1064=== modified file 'run-mac-tests'
1065--- run-mac-tests 2012-05-01 11:56:52 +0000
1066+++ run-mac-tests 2012-07-18 18:54:28 +0000
1067@@ -26,7 +26,6 @@
1068 # do not wish to do so, delete this exception statement from your
1069 # version. If you delete this exception statement from all source
1070 # files in the program, then also delete it here.
1071-GTK_TESTS_PATH=ubuntu_sso/gtk/tests
1072 WINDOWS_TESTS=test_windows.py
1073
1074 set -e
1075@@ -48,8 +47,8 @@
1076 }
1077
1078 echo "*** Running QT test suite for ""$MODULE"" ***"
1079-./setup.py build
1080-python $u1trial --reactor=qt4 --gui -p "$GTK_TESTS_PATH" -i "test_gui.py, test_linux.py, test_qt.py, test_windows.py, test_glib.py, test_txsecrets.py" "$MODULE"
1081+python ./setup.py build
1082+python $u1trial --reactor=qt4 --gui -i "test_linux.py, test_qt.py, test_windows.py, test_glib.py, test_txsecrets.py" "$MODULE"
1083 rm -rf _trial_temp
1084 rm -rf build
1085
1086
1087=== modified file 'run-tests'
1088--- run-tests 2012-05-16 22:06:19 +0000
1089+++ run-tests 2012-07-18 18:54:28 +0000
1090@@ -32,8 +32,6 @@
1091 QT_TESTS_PATH="ubuntu_sso/qt/tests, ubuntu_sso/qt/main/tests"
1092 QT_TEST_FILES="test_qt.py,test_qtwisted.py"
1093
1094-GTK_TESTS_PATH=ubuntu_sso/gtk/tests
1095-
1096 LINUX_IGNORE_FILES="test_darwin.py,test_windows.py,test_pykeyring.py"
1097
1098 set -e
1099@@ -54,7 +52,6 @@
1100 echo "Please install the 'pep8' package."
1101 fi
1102 }
1103-unset GTK_MODULES
1104
1105 XVFB_CMDLINE=""
1106 XVFB=$(which xvfb-run)
1107@@ -62,18 +59,9 @@
1108 XVFB_CMDLINE="$XVFB -a"
1109 fi
1110
1111-echo "*** Running GTK test suite for ""$MODULE"" ***"
1112-$XVFB_CMDLINE u1trial --reactor=gi --gui -p "$QT_TESTS_PATH" -i "$QT_TEST_FILES,$LINUX_IGNORE_FILES" "$MODULE"
1113-rm -rf _trial_temp
1114-
1115-# hack to avoid:
1116-# g_dbus_connection_real_closed: Remote peer vanished with error: Underlying
1117-# GIOStream returned 0 bytes on an async read (g-io-error-quark, 0). Exiting.
1118-sleep 3
1119-
1120 echo "*** Running QT test suite for ""$MODULE"" ***"
1121 ./setup.py build
1122-$XVFB_CMDLINE u1trial --reactor=qt4 --gui -p "$GTK_TESTS_PATH" -i $LINUX_IGNORE_FILES "$MODULE"
1123+$XVFB_CMDLINE u1trial --reactor=qt4 --gui -i $LINUX_IGNORE_FILES "$MODULE"
1124 rm -rf _trial_temp
1125 rm -rf build
1126
1127
1128=== modified file 'run-tests.bat'
1129--- run-tests.bat 2012-05-15 20:41:56 +0000
1130+++ run-tests.bat 2012-07-18 18:54:28 +0000
1131@@ -74,7 +74,7 @@
1132 "%PYTHONEXEPATH%" setup.py build
1133 ECHO Running tests
1134 :: execute the tests with a number of ignored linux only modules
1135-"%PYTHONEXEPATH%" "%TRIALPATH%" -i "test_darwin.py, test_linux.py, test_txsecrets.py, test_qt.py, test_glib.py" -p "ubuntu_sso\gtk" --reactor=qt4 --gui %PARAMS% ubuntu_sso
1136+"%PYTHONEXEPATH%" "%TRIALPATH%" -i "test_darwin.py, test_linux.py, test_txsecrets.py, test_qt.py, test_glib.py" --reactor=qt4 --gui %PARAMS% ubuntu_sso
1137 :: Clean the build from the setupt.py
1138 ECHO Cleaning the generated code before running the style checks...
1139 "%PYTHONEXEPATH%" setup.py clean
1140@@ -84,7 +84,7 @@
1141 GOTO :CLEAN)
1142
1143 ECHO Performing style checks...
1144-SET IGNORE_LINT="ubuntu_sso\gtk,ubuntu_sso\networkstate\linux.py,ubuntu_sso\main\linux.py,ubuntu_sso\main\tests\test_linux.py,ubuntu_sso\utils\txsecrets.py,ubuntu_sso\utils\tests\test_txsecrets.py,ubuntu_sso\tests\bin,bin\ubuntu-sso-login"
1145+SET IGNORE_LINT="ubuntu_sso\networkstate\linux.py,ubuntu_sso\main\linux.py,ubuntu_sso\main\tests\test_linux.py,ubuntu_sso\utils\txsecrets.py,ubuntu_sso\utils\tests\test_txsecrets.py,ubuntu_sso\tests\bin,bin\ubuntu-sso-login"
1146 "%PYTHONEXEPATH%" "%LINTPATH%" -i "%IGNORE_LINT%" ubuntu_sso
1147 :: test for style if we can, if pep8 is not present, move to the end
1148 ECHO Running PEP-8 Check...
1149
1150=== modified file 'setup.py'
1151--- setup.py 2012-06-27 16:04:31 +0000
1152+++ setup.py 2012-07-18 18:54:28 +0000
1153@@ -1,4 +1,4 @@
1154-#!/usr/bin/env python
1155+#!/usr/bin/python
1156 # setup.py - Build system for Ubuntu SSO Client package
1157 #
1158 # Copyright 2010-2012 Canonical Ltd.
1159@@ -237,7 +237,6 @@
1160
1161 sso_executables = [
1162 'bin/ubuntu-sso-login',
1163- 'bin/ubuntu-sso-login-gtk',
1164 'bin/ubuntu-sso-login-qt',
1165 'bin/ubuntu-sso-proxy-creds-qt',
1166 'bin/ubuntu-sso-ssl-certificate-qt',
1167@@ -309,7 +308,6 @@
1168 packages=[
1169 'ubuntu_sso',
1170 'ubuntu_sso.tests',
1171- 'ubuntu_sso.gtk',
1172 'ubuntu_sso.keyring',
1173 'ubuntu_sso.keyring.tests',
1174 'ubuntu_sso.main',
1175
1176=== modified file 'ubuntu_sso/__init__.py'
1177--- ubuntu_sso/__init__.py 2012-04-09 17:38:24 +0000
1178+++ ubuntu_sso/__init__.py 2012-07-18 18:54:28 +0000
1179@@ -28,8 +28,6 @@
1180 # files in the program, then also delete it here.
1181 """Ubuntu Single Sign On client code."""
1182
1183-import sys
1184-
1185 # DBus constants
1186 DBUS_BUS_NAME = "com.ubuntu.sso"
1187
1188@@ -46,11 +44,9 @@
1189 USER_CANCELLATION = 10
1190 EXCEPTION_RAISED = 11
1191
1192+BACKEND_EXECUTABLE = 'ubuntu-sso-login'
1193+
1194 # available UIs
1195-UI_EXECUTABLE_GTK = 'ubuntu-sso-login-gtk'
1196+UI_SSL_DIALOG = 'ubuntu-sso-ssl-certificate-qt'
1197 UI_EXECUTABLE_QT = 'ubuntu-sso-login-qt'
1198 UI_PROXY_CREDS_DIALOG = 'ubuntu-sso-proxy-creds-qt'
1199-
1200-if getattr(sys, "frozen", None) is not None and sys.platform == "win32":
1201- UI_EXECUTABLE_QT += ".exe"
1202- UI_PROXY_CREDS_DIALOG += ".exe"
1203
1204=== modified file 'ubuntu_sso/credentials.py'
1205--- ubuntu_sso/credentials.py 2012-05-04 19:03:04 +0000
1206+++ ubuntu_sso/credentials.py 2012-07-18 18:54:28 +0000
1207@@ -43,21 +43,18 @@
1208
1209 """
1210
1211-import os
1212-
1213 from functools import wraps
1214
1215 from twisted.internet import defer
1216
1217 from ubuntu_sso import (
1218- UI_EXECUTABLE_GTK,
1219 UI_EXECUTABLE_QT,
1220 USER_CANCELLATION,
1221 USER_SUCCESS,
1222 )
1223 from ubuntu_sso.keyring import Keyring
1224 from ubuntu_sso.logger import setup_logging
1225-from ubuntu_sso.utils import get_bin_dir, runner
1226+from ubuntu_sso.utils import get_bin_cmd, runner
1227
1228
1229 logger = setup_logging('ubuntu_sso.credentials')
1230@@ -122,7 +119,7 @@
1231
1232 def __init__(self, app_name, tc_url=None, help_text='',
1233 window_id=0, ping_url=None, policy_url=None,
1234- ui_executable=UI_EXECUTABLE_GTK):
1235+ ui_executable=UI_EXECUTABLE_QT):
1236 """Return a Credentials management object.
1237
1238 'app_name' is the application name to be displayed in the GUI.
1239@@ -167,29 +164,26 @@
1240 Upon analyzing returning code from the UI process, emit proper signals
1241 to the caller.
1242
1243- The caller can specify a preference for the UI (so far either GTK+ or
1244- Qt UIs are available), but if the preferred one is not available, the
1245- service will try to open any available UI.
1246+ The caller can specify a preference for the UI, but if the preferred
1247+ one is not available, the service will try to open any available UI.
1248
1249 If no GUI is available, GUINotAvailableError will be raised.
1250
1251 """
1252- guis = (self.ui_executable, UI_EXECUTABLE_GTK, UI_EXECUTABLE_QT)
1253- for gui_exe_path in guis:
1254- ui_exe_path = os.path.join(get_bin_dir(), gui_exe_path)
1255- ui_exe_exists = os.path.exists(ui_exe_path)
1256- logger.debug('Attempting to use the following executable as GUI: '
1257- '%r (exists? %r).', ui_exe_path, ui_exe_exists)
1258- if not ui_exe_exists:
1259- logger.error('The given UI path %r does not exist.',
1260- ui_exe_path)
1261+ guis = (self.ui_executable, UI_EXECUTABLE_QT)
1262+ for gui_exe_name in guis:
1263+ try:
1264+ args = get_bin_cmd(gui_exe_name)
1265+ except OSError:
1266+ logger.error('The given UI %r does not exist.',
1267+ gui_exe_name)
1268 else:
1269 break
1270 else:
1271 raise GUINotAvailableError('Can not find a GUI to present to the '
1272- 'user (tried with %r). Aborting.' % repr(guis))
1273+ 'user (tried with %r). Aborting.' %
1274+ repr(guis))
1275
1276- args = [ui_exe_path]
1277 for arg in ('app_name', 'help_text', 'ping_url', 'policy_url',
1278 'tc_url', 'window_id'):
1279 value = getattr(self, arg)
1280
1281=== removed directory 'ubuntu_sso/gtk'
1282=== removed file 'ubuntu_sso/gtk/__init__.py'
1283--- ubuntu_sso/gtk/__init__.py 2012-04-09 17:38:24 +0000
1284+++ ubuntu_sso/gtk/__init__.py 1970-01-01 00:00:00 +0000
1285@@ -1,29 +0,0 @@
1286-# -*- coding: utf-8 -*-
1287-#
1288-# Copyright 2009-2012 Canonical Ltd.
1289-#
1290-# This program is free software: you can redistribute it and/or modify it
1291-# under the terms of the GNU General Public License version 3, as published
1292-# by the Free Software Foundation.
1293-#
1294-# This program is distributed in the hope that it will be useful, but
1295-# WITHOUT ANY WARRANTY; without even the implied warranties of
1296-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1297-# PURPOSE. See the GNU General Public License for more details.
1298-#
1299-# You should have received a copy of the GNU General Public License along
1300-# with this program. If not, see <http://www.gnu.org/licenses/>.
1301-#
1302-# In addition, as a special exception, the copyright holders give
1303-# permission to link the code of portions of this program with the
1304-# OpenSSL library under certain conditions as described in each
1305-# individual source file, and distribute linked combinations
1306-# including the two.
1307-# You must obey the GNU General Public License in all respects
1308-# for all of the code used other than OpenSSL. If you modify
1309-# file(s) with this exception, you may extend this exception to your
1310-# version of the file(s), but you are not obligated to do so. If you
1311-# do not wish to do so, delete this exception statement from your
1312-# version. If you delete this exception statement from all source
1313-# files in the program, then also delete it here.
1314-"""Ubuntu Single Sign On graphical interface."""
1315
1316=== removed file 'ubuntu_sso/gtk/gui.py'
1317--- ubuntu_sso/gtk/gui.py 2012-06-22 20:28:49 +0000
1318+++ ubuntu_sso/gtk/gui.py 1970-01-01 00:00:00 +0000
1319@@ -1,1152 +0,0 @@
1320-# -*- coding: utf-8 -*-
1321-#
1322-# Copyright 2010-2012 Canonical Ltd.
1323-#
1324-# This program is free software: you can redistribute it and/or modify it
1325-# under the terms of the GNU General Public License version 3, as published
1326-# by the Free Software Foundation.
1327-#
1328-# This program is distributed in the hope that it will be useful, but
1329-# WITHOUT ANY WARRANTY; without even the implied warranties of
1330-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1331-# PURPOSE. See the GNU General Public License for more details.
1332-#
1333-# You should have received a copy of the GNU General Public License along
1334-# with this program. If not, see <http://www.gnu.org/licenses/>.
1335-#
1336-# In addition, as a special exception, the copyright holders give
1337-# permission to link the code of portions of this program with the
1338-# OpenSSL library under certain conditions as described in each
1339-# individual source file, and distribute linked combinations
1340-# including the two.
1341-# You must obey the GNU General Public License in all respects
1342-# for all of the code used other than OpenSSL. If you modify
1343-# file(s) with this exception, you may extend this exception to your
1344-# version of the file(s), but you are not obligated to do so. If you
1345-# do not wish to do so, delete this exception statement from your
1346-# version. If you delete this exception statement from all source
1347-# files in the program, then also delete it here.
1348-"""User registration GUI."""
1349-
1350-import logging
1351-import os
1352-import sys
1353-import tempfile
1354-import webbrowser
1355-
1356-from functools import wraps, partial
1357-
1358-# pylint: disable=E0611,F0401
1359-from gi.repository import Gdk, Gtk
1360-from gi.repository.GdkX11 import X11Window
1361-# pylint: enable=E0611,F0401
1362-from twisted.internet import defer
1363-
1364-from ubuntu_sso import (
1365- main,
1366- NO_OP,
1367- USER_CANCELLATION,
1368- USER_SUCCESS,
1369- utils,
1370-)
1371-from ubuntu_sso.logger import setup_gui_logging
1372-from ubuntu_sso.utils import get_data_file
1373-from ubuntu_sso.utils.ui import (
1374- CAPTCHA_LOAD_ERROR,
1375- CAPTCHA_RELOAD_TOOLTIP,
1376- CONNECT_HELP_LABEL,
1377- EMAIL_MISMATCH,
1378- EMAIL_INVALID,
1379- ERROR,
1380- FIELD_REQUIRED,
1381- FORGOTTEN_PASSWORD_BUTTON,
1382- GENERIC_BACKEND_ERROR,
1383- is_min_required_password,
1384- is_correct_email,
1385- JOIN_HEADER_LABEL,
1386- LOADING,
1387- LOGIN_BUTTON_LABEL,
1388- LOGIN_HEADER_LABEL,
1389- NEXT,
1390- ONE_MOMENT_PLEASE,
1391- PASSWORD_CHANGED,
1392- PASSWORD_HELP,
1393- PASSWORD_MISMATCH,
1394- PASSWORD_TOO_WEAK,
1395- REQUEST_PASSWORD_TOKEN_LABEL,
1396- RESET_PASSWORD,
1397- SET_NEW_PASSWORD_LABEL,
1398- SUCCESS,
1399- TC_BUTTON,
1400- TC_NOT_ACCEPTED,
1401- VERIFY_EMAIL_LABEL,
1402- YES_TO_TC,
1403- YES_TO_UPDATES,
1404-)
1405-
1406-
1407-# Instance of 'UbuntuSSOClientGUI' has no 'yyy' member
1408-# pylint: disable=E1101
1409-
1410-
1411-logger = setup_gui_logging('ubuntu_sso.gui.gtk')
1412-
1413-
1414-# pylint: disable=C0103
1415-def parse_color(color):
1416- """Parse a string color into Gdk.Color."""
1417- c = Gdk.RGBA()
1418- result = c.parse(color)
1419- if not result:
1420- logger.warning('Could not parse color %r.', color)
1421- return c
1422-# pylint: enable=C0103
1423-
1424-DEFAULT_WIDTH = 30
1425-# To be replaced by values from the theme (LP: #616526)
1426-HELP_TEXT_COLOR = parse_color("#bfbfbf")
1427-WARNING_TEXT_COLOR = parse_color("red")
1428-LARGE_MARKUP = u'<span size="x-large">%s</span>'
1429-
1430-
1431-# SSL properties and certs location
1432-STRICT_SSL_PROP = 'ssl-strict'
1433-CERTS_FILE_PROP = 'ssl-ca-file'
1434-CA_CERT_FILE = '/etc/ssl/certs/ca-certificates.crt'
1435-
1436-
1437-def log_call(f):
1438- """Decorator to log call funtions."""
1439-
1440- @wraps(f)
1441- def inner(*args, **kwargs):
1442- """Execute 'f' logging the call as INFO."""
1443- logger.info('%s: args %r, kwargs %r.', f.__name__, args, kwargs)
1444- return f(*args, **kwargs)
1445-
1446- return inner
1447-
1448-
1449-class LabeledEntry(Gtk.Entry):
1450- """An entry that displays the label within itself ina grey color."""
1451-
1452- # Use of super on an old style class
1453- # pylint: disable=E1002
1454-
1455- def __init__(self, label, is_password=False, *args, **kwargs):
1456- self.label = label
1457- self.is_password = is_password
1458- self.warning = None
1459-
1460- super(LabeledEntry, self).__init__(*args, **kwargs)
1461-
1462- self.set_width_chars(DEFAULT_WIDTH)
1463- self._set_label(self, None)
1464- self.set_tooltip_text(self.label)
1465- self.connect('focus-in-event', self._clear_text)
1466- self.connect('focus-out-event', self._set_label)
1467- self.clear_warning()
1468- self.show()
1469-
1470- def _clear_text(self, *args, **kwargs):
1471- """Clear text and restore text color."""
1472- self.set_text(self.get_text())
1473-
1474- # restore to theme's default
1475- self.override_color(Gtk.StateFlags.NORMAL, None)
1476-
1477- if self.is_password:
1478- self.set_visibility(False)
1479-
1480- return False # propagate the event further
1481-
1482- def _set_label(self, *args, **kwargs):
1483- """Set the proper label and proper coloring."""
1484- if self.get_text():
1485- return
1486-
1487- self.set_text(self.label)
1488- self.override_color(Gtk.StateFlags.NORMAL, HELP_TEXT_COLOR)
1489-
1490- if self.is_password:
1491- self.set_visibility(True)
1492-
1493- return False # propagate the event further
1494-
1495- def get_text(self):
1496- """Get text only if it's not the label nor empty."""
1497- result = super(LabeledEntry, self).get_text().decode('utf8')
1498- if result == self.label or result.isspace():
1499- result = u''
1500- return result
1501-
1502- def set_warning(self, warning_msg):
1503- """Display warning as secondary icon, set 'warning_msg' as tooltip."""
1504- self.warning = warning_msg
1505- self.set_property('secondary-icon-stock', Gtk.STOCK_DIALOG_WARNING)
1506- self.set_property('secondary-icon-sensitive', True)
1507- self.set_property('secondary-icon-activatable', False)
1508- self.set_property('secondary-icon-tooltip-text', warning_msg)
1509-
1510- def clear_warning(self):
1511- """Remove any warning."""
1512- self.warning = None
1513- self.set_property('secondary-icon-stock', None)
1514- self.set_property('secondary-icon-sensitive', False)
1515- self.set_property('secondary-icon-activatable', False)
1516- self.set_property('secondary-icon-tooltip-text', None)
1517-
1518-
1519-class UbuntuSSOClientGUI(object):
1520- """Ubuntu single sign-on GUI."""
1521-
1522- def __init__(self, app_name, **kwargs):
1523- """Create the GUI and initialize widgets."""
1524- logger.debug('UbuntuSSOClientGUI: app_name %r, kwargs %r.',
1525- app_name, kwargs)
1526-
1527- self._captcha_filename = tempfile.mktemp()
1528- self._captcha_id = None
1529- self._signals_receivers = {}
1530- self._done = False # whether the whole process was completed or not
1531-
1532- self.app_name = app_name
1533- self.app_label = u'<b>%s</b>' % self.app_name
1534- self.ping_url = kwargs.get('ping_url', u'')
1535- self.tc_url = kwargs.get('tc_url', u'')
1536- self.help_text = kwargs.get('help_text', u'')
1537- self.login_only = kwargs.get('login_only', False)
1538- window_id = kwargs.get('window_id', 0)
1539- self.close_callback = kwargs.get('close_callback', NO_OP)
1540- self.backend = None
1541- self.user_email = None
1542- self.user_password = None
1543-
1544- ui_filename = get_data_file('gtk', 'ui.glade')
1545- builder = Gtk.Builder()
1546- builder.add_from_file(ui_filename)
1547- builder.connect_signals(self)
1548-
1549- self.widgets = []
1550- self.warnings = []
1551- self.cancels = []
1552- for obj in builder.get_objects():
1553- name = getattr(obj, 'name', None)
1554- if name is None and isinstance(obj, Gtk.Buildable):
1555- # work around bug lp:507739
1556- name = Gtk.Buildable.get_name(obj)
1557- if name is None:
1558- logging.warn("%s has no name (??)", obj)
1559- else:
1560- self.widgets.append(name)
1561- setattr(self, name, obj)
1562- if 'warning' in name:
1563- self.warnings.append(obj)
1564- obj.set_text('')
1565- if 'cancel_button' in name:
1566- obj.connect('clicked', self.on_close_clicked)
1567- self.cancels.append(obj)
1568-
1569- # Connect the activate-link signal here
1570- # GtkBuilder in GTK 3 seems to not do this
1571- self.login_button.connect('activate-link', self.on_activate_link)
1572- self.forgotten_password_button.connect('activate-link',
1573- self.on_activate_link)
1574-
1575- self.entries = (u'name_entry', u'email1_entry', u'email2_entry',
1576- u'password1_entry', u'password2_entry',
1577- u'captcha_solution_entry', u'email_token_entry',
1578- u'login_email_entry', u'login_password_entry',
1579- u'reset_email_entry', u'reset_code_entry',
1580- u'reset_password1_entry', u'reset_password2_entry')
1581-
1582- for name in self.entries:
1583- label = getattr(utils.ui, name.upper())
1584- is_password = 'password' in name
1585- entry = LabeledEntry(label=label, is_password=is_password)
1586- entry.set_activates_default(True)
1587- setattr(self, name, entry)
1588-
1589- self.window.set_icon_name('ubuntu-logo')
1590-
1591- self.pages = (self.enter_details_vbox, self.processing_vbox,
1592- self.verify_email_vbox, self.finish_vbox,
1593- self.tc_browser_vbox, self.login_vbox,
1594- self.request_password_token_vbox,
1595- self.set_new_password_vbox)
1596-
1597- self._signals = {
1598- 'CaptchaGenerated':
1599- self._filter_by_app_name(self.on_captcha_generated),
1600- 'CaptchaGenerationError':
1601- self._filter_by_app_name(self.on_captcha_generation_error),
1602- 'UserRegistered':
1603- self._filter_by_app_name(self.on_user_registered),
1604- 'UserRegistrationError':
1605- self._filter_by_app_name(self.on_user_registration_error),
1606- 'EmailValidated':
1607- self._filter_by_app_name(self.on_email_validated),
1608- 'EmailValidationError':
1609- self._filter_by_app_name(self.on_email_validation_error),
1610- 'LoggedIn':
1611- self._filter_by_app_name(self.on_logged_in),
1612- 'LoginError':
1613- self._filter_by_app_name(self.on_login_error),
1614- 'UserNotValidated':
1615- self._filter_by_app_name(self.on_user_not_validated),
1616- 'PasswordResetTokenSent':
1617- self._filter_by_app_name(self.on_password_reset_token_sent),
1618- 'PasswordResetError':
1619- self._filter_by_app_name(self.on_password_reset_error),
1620- 'PasswordChanged':
1621- self._filter_by_app_name(self.on_password_changed),
1622- 'PasswordChangeError':
1623- self._filter_by_app_name(self.on_password_change_error),
1624- }
1625-
1626- if window_id != 0:
1627- # be as robust as possible:
1628- # if the window_id is not "good", set_transient_for will fail
1629- # awfully, and we don't want that: if the window_id is bad we can
1630- # still do everything as a standalone window. Also,
1631- # window_foreign_new may return None breaking set_transient_for.
1632- try:
1633- display = Gdk.Display.get_default()
1634- # this is not working, we need to create a XLib.window
1635- # as a second parameter to foreign_new_for_display
1636- win = X11Window.foreign_new_for_display(display, None)
1637- self.window.realize()
1638- self.window.window.set_transient_for(win)
1639- except: # pylint: disable=W0702
1640- msg = 'UbuntuSSOClientGUI: failed set_transient_for win id %r'
1641- logger.exception(msg, window_id)
1642-
1643- self.yes_to_updates_checkbutton.hide()
1644- self.start_backend()
1645-
1646- @defer.inlineCallbacks
1647- def start_backend(self):
1648- """Start the backend, show the window when ready."""
1649- client = yield main.get_sso_client()
1650- self.backend = client.sso_login
1651-
1652- logger.debug('UbuntuSSOClientGUI: backend created: %r', self.backend)
1653-
1654- self._setup_signals()
1655- self._append_pages()
1656- self.window.show()
1657-
1658- @property
1659- def success_vbox(self):
1660- """The success page."""
1661- message = SUCCESS % {'app_name': self.app_name}
1662- message = LARGE_MARKUP % message
1663- self.finish_vbox.label.set_markup(message)
1664- return self.finish_vbox
1665-
1666- @property
1667- def error_vbox(self):
1668- """The error page."""
1669- self.finish_vbox.label.set_markup(LARGE_MARKUP % ERROR)
1670- return self.finish_vbox
1671-
1672- # helpers
1673-
1674- def _filter_by_app_name(self, f):
1675- """Excecute the decorated function only for 'self.app_name'."""
1676-
1677- @wraps(f)
1678- def inner(app_name, *args, **kwargs):
1679- """Execute 'f' only if 'app_name' matches 'self.app_name'."""
1680- result = None
1681- if app_name == self.app_name:
1682- result = f(app_name, *args, **kwargs)
1683- else:
1684- logger.info('%s: ignoring call since received app_name '
1685- '%r (expected %r)',
1686- f.__name__, app_name, self.app_name)
1687- return result
1688-
1689- return inner
1690-
1691- def _setup_signals(self):
1692- """Bind signals to callbacks to be able to test the pages."""
1693- for signal, method in self._signals.items():
1694- actual = self._signals_receivers.get(signal)
1695- if actual is not None:
1696- msg = 'Signal %r is already connected with %r.'
1697- logger.warning(msg, signal, actual)
1698-
1699- match = self.backend.connect_to_signal(signal, method)
1700- self._signals_receivers[signal] = match
1701-
1702- def _add_spinner_to_container(self, container, legend=None):
1703- """Add a spinner to 'container'."""
1704- spinner = Gtk.Spinner()
1705- spinner.start()
1706-
1707- label = Gtk.Label()
1708- if legend:
1709- label.set_text(legend)
1710- else:
1711- label.set_text(LOADING)
1712-
1713- hbox = Gtk.HBox(spacing=5)
1714- hbox.pack_start(spinner, expand=False, fill=True, padding=0)
1715- hbox.pack_start(label, expand=False, fill=True, padding=0)
1716-
1717- alignment = Gtk.Alignment(xalign=0.5, yalign=0.5,
1718- xscale=0, yscale=0)
1719- alignment.add(hbox)
1720- alignment.show_all()
1721-
1722- # remove children to avoid:
1723- # GtkWarning: Attempting to add a widget with type GtkAlignment to a
1724- # GtkEventBox, but as a GtkBin subclass a GtkEventBox can only contain
1725- # one widget at a time
1726- for child in container.get_children():
1727- container.remove(child)
1728- container.add(alignment)
1729-
1730- def _set_warning_message(self, widget, message):
1731- """Set 'message' as text for 'widget'."""
1732- widget.set_text(message)
1733- widget.override_color(Gtk.StateFlags.NORMAL, WARNING_TEXT_COLOR)
1734- widget.show()
1735-
1736- def _clear_warnings(self):
1737- """Clear all warning messages."""
1738- for widget in self.warnings:
1739- widget.set_text('')
1740- for widget in self.entries:
1741- getattr(self, widget).clear_warning()
1742-
1743- def _non_empty_input(self, widget):
1744- """Return weather widget has non empty content."""
1745- text = widget.get_text()
1746- return bool(text and not text.isspace())
1747-
1748- def _handle_error(self, remote_call, handler, error):
1749- """Handle any error when calling the remote backend."""
1750- logger.error('Remote call %r failed with: %r', remote_call, error)
1751- errordict = {'message': GENERIC_BACKEND_ERROR}
1752- handler(self.app_name, errordict)
1753-
1754- # build pages
1755-
1756- def _append_pages(self):
1757- """Append all the requires pages to main widget."""
1758- self._append_page(self._build_processing_page())
1759- self._append_page(self._build_finish_page())
1760- self._append_page(self._build_login_page())
1761- self._append_page(self._build_request_password_token_page())
1762- self._append_page(self._build_set_new_password_page())
1763- self._append_page(self._build_verify_email_page())
1764-
1765- if not self.login_only:
1766- self._append_page(self._build_enter_details_page())
1767- self._append_page(self._build_tc_page())
1768- self.login_button.grab_focus()
1769- self._set_current_page(self.enter_details_vbox)
1770- else:
1771- self.login_back_button.hide()
1772- self.login_ok_button.grab_focus()
1773- self.login_vbox.help_text = self.help_text
1774- self._set_current_page(self.login_vbox)
1775-
1776- def _append_page(self, page):
1777- """Append 'page' to the 'window'."""
1778- self.content.append_page(page, None)
1779-
1780- def _set_header(self, header):
1781- """Set 'header' as the window title and header."""
1782- self.header_label.set_markup(LARGE_MARKUP % header)
1783- self.window.set_title(self.header_label.get_text()) # avoid markup
1784-
1785- def _set_current_page(self, current_page, warning_text=None):
1786- """Hide all the pages but 'current_page'."""
1787- if hasattr(current_page, 'header'):
1788- self._set_header(current_page.header)
1789-
1790- if hasattr(current_page, 'help_text'):
1791- self.help_label.set_markup(current_page.help_text)
1792-
1793- if warning_text is not None:
1794- self._set_warning_message(self.warning_label, warning_text)
1795- else:
1796- self.warning_label.set_text('')
1797-
1798- self.content.set_current_page(self.content.page_num(current_page))
1799-
1800- if current_page.default_widget is not None:
1801- current_page.default_widget.grab_default()
1802-
1803- def _generate_captcha(self):
1804- """Ask for a new captcha; update the ui to reflect the fact."""
1805- logger.info('Calling generate_captcha with filename path at %r',
1806- self._captcha_filename)
1807- self.warning_label.set_text('')
1808- f = self.backend.generate_captcha
1809- error_handler = partial(self._handle_error, f,
1810- self.on_captcha_generation_error)
1811- f(self.app_name, self._captcha_filename,
1812- reply_handler=NO_OP, error_handler=error_handler)
1813- self._set_captcha_loading()
1814-
1815- def _set_captcha_loading(self):
1816- """Present a spinner to the user while the captcha is downloaded."""
1817- self.captcha_image.hide()
1818- self._add_spinner_to_container(self.captcha_loading)
1819- self.captcha_loading.override_background_color(Gtk.StateFlags.NORMAL,
1820- parse_color('white'))
1821- self.captcha_loading.show_all()
1822- self.join_ok_button.set_sensitive(False)
1823-
1824- def _set_captcha_image(self):
1825- """Present a captcha image to the user to be resolved."""
1826- self.captcha_loading.hide()
1827- self.join_ok_button.set_sensitive(True)
1828- self.captcha_image.set_from_file(self._captcha_filename)
1829- self.captcha_image.show()
1830-
1831- def _build_enter_details_page(self):
1832- """Build the enter details page."""
1833- d = {'app_name': self.app_label}
1834- self.enter_details_vbox.header = JOIN_HEADER_LABEL % d
1835- self.enter_details_vbox.help_text = self.help_text
1836- self.enter_details_vbox.default_widget = self.join_ok_button
1837- self.join_ok_button.set_can_default(True)
1838-
1839- self.enter_details_vbox.pack_start(self.name_entry,
1840- expand=False, fill=True, padding=0)
1841- self.enter_details_vbox.reorder_child(self.name_entry, 0)
1842- entry = self.captcha_solution_entry
1843- self.captcha_solution_vbox.pack_start(entry,
1844- expand=False, fill=True, padding=0)
1845- msg = CAPTCHA_RELOAD_TOOLTIP
1846- self.captcha_reload_button.set_tooltip_text(msg)
1847-
1848- self.emails_hbox.pack_start(self.email1_entry,
1849- expand=False, fill=True, padding=0)
1850- self.emails_hbox.pack_start(self.email2_entry,
1851- expand=False, fill=True, padding=0)
1852-
1853- self.passwords_hbox.pack_start(self.password1_entry,
1854- expand=False, fill=True, padding=0)
1855- self.passwords_hbox.pack_start(self.password2_entry,
1856- expand=False, fill=True, padding=0)
1857- help_msg = '<small>%s</small>' % PASSWORD_HELP
1858- self.password_help_label.set_markup(help_msg)
1859-
1860- if not os.path.exists(self._captcha_filename):
1861- self._generate_captcha()
1862- else:
1863- self._set_captcha_image()
1864-
1865- msg = YES_TO_UPDATES % {'app_name': self.app_name}
1866- self.yes_to_updates_checkbutton.set_label(msg)
1867-
1868- msg = YES_TO_TC % {'app_name': self.app_name}
1869- self.yes_to_tc_checkbutton.set_label(msg)
1870- self.tc_button.set_label(TC_BUTTON)
1871-
1872- if not self.tc_url:
1873- self.tc_vbox.hide()
1874- self.login_button.set_label(LOGIN_BUTTON_LABEL)
1875-
1876- return self.enter_details_vbox
1877-
1878- def _build_tc_page(self):
1879- """Build the Terms & Conditions page."""
1880- self.tc_browser_vbox.help_text = ''
1881- self.tc_browser_vbox.default_widget = self.tc_back_button
1882- self.tc_browser_vbox.default_widget.set_can_default(True)
1883- return self.tc_browser_vbox
1884-
1885- def _build_processing_page(self):
1886- """Build the processing page with a spinner."""
1887- self.processing_vbox.default_widget = None
1888- self._add_spinner_to_container(self.processing_vbox,
1889- legend=ONE_MOMENT_PLEASE)
1890- return self.processing_vbox
1891-
1892- def _build_verify_email_page(self):
1893- """Build the verify email page."""
1894- self.verify_email_vbox.default_widget = self.verify_token_button
1895- self.verify_email_vbox.default_widget.set_can_default(True)
1896-
1897- self.verify_email_details_vbox.pack_start(self.email_token_entry,
1898- expand=False, fill=True, padding=0)
1899- return self.verify_email_vbox
1900-
1901- def _build_finish_page(self):
1902- """Build the success page."""
1903- self.finish_vbox.default_widget = self.finish_close_button
1904- self.finish_vbox.default_widget.set_can_default(True)
1905- self.finish_vbox.label = self.finish_label
1906- return self.finish_vbox
1907-
1908- def _build_login_page(self):
1909- """Build the login page."""
1910- d = {'app_name': self.app_label}
1911- self.login_vbox.header = LOGIN_HEADER_LABEL % d
1912- self.login_vbox.help_text = CONNECT_HELP_LABEL % d
1913- self.login_vbox.default_widget = self.login_ok_button
1914- self.login_vbox.default_widget.set_can_default(True)
1915-
1916- self.login_details_vbox.pack_start(self.login_email_entry,
1917- expand=True, fill=True, padding=0)
1918- self.login_details_vbox.reorder_child(self.login_email_entry, 0)
1919- self.login_details_vbox.pack_start(self.login_password_entry,
1920- expand=True, fill=True, padding=0)
1921- self.login_details_vbox.reorder_child(self.login_password_entry, 1)
1922-
1923- msg = FORGOTTEN_PASSWORD_BUTTON
1924- self.forgotten_password_button.set_label(msg)
1925- self.login_ok_button.grab_focus()
1926-
1927- return self.login_vbox
1928-
1929- def _build_request_password_token_page(self):
1930- """Build the login page."""
1931- self.request_password_token_vbox.header = RESET_PASSWORD
1932- text = REQUEST_PASSWORD_TOKEN_LABEL % {'app_name': self.app_label}
1933- self.request_password_token_vbox.help_text = text
1934- btn = self.request_password_token_ok_button
1935- btn.set_can_default(True)
1936- self.request_password_token_vbox.default_widget = btn
1937-
1938- entry = self.reset_email_entry
1939- self.request_password_token_details_vbox.pack_start(entry,
1940- expand=False, fill=True, padding=0)
1941- cb = self.on_reset_email_entry_changed
1942- self.reset_email_entry.connect('changed', cb)
1943- self.request_password_token_ok_button.set_label(NEXT)
1944- self.request_password_token_ok_button.set_sensitive(False)
1945-
1946- return self.request_password_token_vbox
1947-
1948- def _build_set_new_password_page(self):
1949- """Build the login page."""
1950- self.set_new_password_vbox.header = RESET_PASSWORD
1951- self.set_new_password_vbox.help_text = SET_NEW_PASSWORD_LABEL
1952- btn = self.set_new_password_ok_button
1953- btn.set_can_default(True)
1954- self.set_new_password_vbox.default_widget = btn
1955-
1956- for entry in (self.reset_code_entry,
1957- self.reset_password1_entry,
1958- self.reset_password2_entry):
1959- self.set_new_password_details_vbox.pack_start(entry,
1960- expand=False, fill=True, padding=0)
1961-
1962- cb = self.on_set_new_password_entries_changed
1963- self.reset_code_entry.connect('changed', cb)
1964- self.reset_password1_entry.connect('changed', cb)
1965- self.reset_password2_entry.connect('changed', cb)
1966- help_msg = '<small>%s</small>' % PASSWORD_HELP
1967- self.reset_password_help_label.set_markup(help_msg)
1968-
1969- self.set_new_password_ok_button.set_label(RESET_PASSWORD)
1970- self.set_new_password_ok_button.set_sensitive(False)
1971-
1972- return self.set_new_password_vbox
1973-
1974- def _validate_email(self, email1, email2=None):
1975- """Validate 'email1', return error message if not valid.
1976-
1977- If 'email2' is given, must match 'email1'.
1978- """
1979- if email2 is not None and email1 != email2:
1980- return EMAIL_MISMATCH
1981-
1982- if not email1:
1983- return FIELD_REQUIRED
1984-
1985- if not is_correct_email(email1):
1986- return EMAIL_INVALID
1987-
1988- def _validate_password(self, password1, password2=None):
1989- """Validate 'password1', return error message if not valid.
1990-
1991- If 'password2' is given, must match 'email1'.
1992- """
1993- if password2 is not None and password1 != password2:
1994- return PASSWORD_MISMATCH
1995-
1996- if not is_min_required_password(password1):
1997- return PASSWORD_TOO_WEAK
1998-
1999- # GTK callbacks
2000-
2001- def destroy(self):
2002- """Destroy this UI."""
2003- self.window.hide()
2004- self.window.destroy()
2005-
2006- def connect(self, signal_name, handler, *args, **kwargs):
2007- """Connect 'signal_name' with 'handler'."""
2008- logger.debug('connect: signal %r, handler %r, args %r, kwargs, %r',
2009- signal_name, handler, args, kwargs)
2010- self.window.connect(signal_name, handler, *args, **kwargs)
2011-
2012- def finish_success(self):
2013- """The whole process was completed succesfully. Show success page."""
2014- self._done = True
2015- self._set_current_page(self.success_vbox)
2016-
2017- def finish_error(self):
2018- """The whole process was not completed succesfully. Show error page."""
2019- self._done = True
2020- self._set_current_page(self.error_vbox)
2021-
2022- def on_activate_link(self, button):
2023- """Do nothing, used for LinkButtons that are used as regular ones."""
2024- return True
2025-
2026- def on_close_clicked(self, *args, **kwargs):
2027- """Call self.close_callback if defined."""
2028- if os.path.exists(self._captcha_filename):
2029- os.remove(self._captcha_filename)
2030-
2031- for signal, match in self._signals_receivers.items():
2032- self.backend.disconnect_from_signal(signal, match)
2033-
2034- # hide the main window
2035- if self.window is not None:
2036- self.window.hide()
2037-
2038- # process any pending events before callbacking with result
2039- while Gtk.events_pending():
2040- Gtk.main_iteration()
2041-
2042- return_code = USER_SUCCESS
2043- if not self._done:
2044- return_code = USER_CANCELLATION
2045- logger.info('Return code will be %r.', return_code)
2046-
2047- # call user defined callback
2048- logger.debug('Calling custom close_callback %r with params %r, %r',
2049- self.close_callback, args, kwargs)
2050- self.close_callback(*args, **kwargs)
2051-
2052- sys.exit(return_code)
2053-
2054- def on_sign_in_button_clicked(self, *args, **kwargs):
2055- """User wants to sign in, present the Login page."""
2056- self._set_current_page(self.login_vbox)
2057-
2058- def on_join_ok_button_clicked(self, *args, **kwargs):
2059- """Submit info for processing, present the processing vbox."""
2060- if not self.join_ok_button.is_sensitive():
2061- return
2062-
2063- self._clear_warnings()
2064-
2065- error = False
2066-
2067- name = self.name_entry.get_text()
2068- if not name:
2069- self.name_entry.set_warning(FIELD_REQUIRED)
2070- logger.warning('on_join_ok_button_clicked: name not set.')
2071- error = True
2072-
2073- # check email
2074- email1 = self.email1_entry.get_text()
2075- email2 = self.email2_entry.get_text()
2076- msg = self._validate_email(email1, email2)
2077- if msg is not None:
2078- self.email1_entry.set_warning(msg)
2079- self.email2_entry.set_warning(msg)
2080- logger.warning('on_join_ok_button_clicked: email is not valid.')
2081- error = True
2082-
2083- # check password
2084- password1 = self.password1_entry.get_text()
2085- password2 = self.password2_entry.get_text()
2086- msg = self._validate_password(password1, password2)
2087- if msg is not None:
2088- self.password1_entry.set_warning(msg)
2089- self.password2_entry.set_warning(msg)
2090- logger.warning('on_join_ok_button_clicked: password is not valid.')
2091- error = True
2092-
2093- # check T&C
2094- if self.tc_url and not self.yes_to_tc_checkbutton.get_active():
2095- self._set_warning_message(self.tc_warning_label,
2096- TC_NOT_ACCEPTED % {'app_name': self.app_name})
2097- logger.warning('on_join_ok_button_clicked: terms and conditions '
2098- 'not accepted.')
2099- error = True
2100-
2101- captcha_solution = self.captcha_solution_entry.get_text()
2102- if not captcha_solution:
2103- self.captcha_solution_entry.set_warning(FIELD_REQUIRED)
2104- logger.warning('on_join_ok_button_clicked: captcha solution not '
2105- 'set.')
2106- error = True
2107-
2108- if error:
2109- logger.warning('on_join_ok_button_clicked: validation failed.')
2110- return
2111-
2112- logger.info('on_join_ok_button_clicked: validation success!')
2113-
2114- self._set_current_page(self.processing_vbox)
2115- self.user_email = email1
2116- self.user_password = password1
2117-
2118- logger.info('Calling register_user with email %r, password <hidden>,'
2119- ' name %r, captcha_id %r and captcha_solution %r.', email1,
2120- name, self._captcha_id, captcha_solution)
2121-
2122- f = self.backend.register_user
2123- error_handler = partial(self._handle_error, f,
2124- self.on_user_registration_error)
2125- f(self.app_name, self.user_email, self.user_password, name,
2126- self._captcha_id, captcha_solution,
2127- reply_handler=NO_OP, error_handler=error_handler)
2128-
2129- def on_verify_token_button_clicked(self, *args, **kwargs):
2130- """The user entered the email token, let's verify!"""
2131- if not self.verify_token_button.is_sensitive():
2132- return
2133-
2134- self._clear_warnings()
2135-
2136- email_token = self.email_token_entry.get_text()
2137- if not email_token:
2138- self.email_token_entry.set_warning(FIELD_REQUIRED)
2139- return
2140-
2141- email = self.user_email
2142- password = self.user_password
2143-
2144- args = (self.app_name, email, password, email_token)
2145- if self.ping_url:
2146- f = self.backend.validate_email_and_ping
2147- args = args + (self.ping_url,)
2148- else:
2149- f = self.backend.validate_email
2150-
2151- logger.info('Calling validate_email with email %r, password <hidden>, '
2152- 'app_name %r and email_token %r.', email, self.app_name,
2153- email_token)
2154- error_handler = partial(self._handle_error, f,
2155- self.on_email_validation_error)
2156- f(*args, reply_handler=NO_OP, error_handler=error_handler)
2157-
2158- self._set_current_page(self.processing_vbox)
2159-
2160- def on_login_connect_button_clicked(self, *args, **kwargs):
2161- """User wants to connect!"""
2162- if not self.login_ok_button.is_sensitive():
2163- return
2164-
2165- self._clear_warnings()
2166-
2167- error = False
2168-
2169- email = self.login_email_entry.get_text()
2170- msg = self._validate_email(email)
2171- if msg is not None:
2172- self.login_email_entry.set_warning(msg)
2173- error = True
2174-
2175- password = self.login_password_entry.get_text()
2176- if not password:
2177- self.login_password_entry.set_warning(FIELD_REQUIRED)
2178- error = True
2179-
2180- if error:
2181- return
2182-
2183- args = (self.app_name, email, password)
2184- if self.ping_url:
2185- f = self.backend.login_and_ping
2186- args = args + (self.ping_url,)
2187- else:
2188- f = self.backend.login
2189-
2190- error_handler = partial(self._handle_error, f, self.on_login_error)
2191- f(*args, reply_handler=NO_OP, error_handler=error_handler)
2192-
2193- self._set_current_page(self.processing_vbox)
2194- self.user_email = email
2195- self.user_password = password
2196-
2197- def on_login_back_button_clicked(self, *args, **kwargs):
2198- """User wants to go to the previous page."""
2199- self._set_current_page(self.enter_details_vbox)
2200-
2201- def on_forgotten_password_button_clicked(self, *args, **kwargs):
2202- """User wants to reset the password."""
2203- self._set_current_page(self.request_password_token_vbox)
2204-
2205- def on_request_password_token_ok_button_clicked(self, *args, **kwargs):
2206- """User entered the email address to reset the password."""
2207- if not self.request_password_token_ok_button.is_sensitive():
2208- return
2209-
2210- self._clear_warnings()
2211-
2212- email = self.reset_email_entry.get_text()
2213- msg = self._validate_email(email)
2214- if msg is not None:
2215- self.reset_email_entry.set_warning(msg)
2216- return
2217-
2218- logger.info('Calling request_password_reset_token with %r.', email)
2219- f = self.backend.request_password_reset_token
2220- error_handler = partial(self._handle_error, f,
2221- self.on_password_reset_error)
2222- f(self.app_name, email,
2223- reply_handler=NO_OP, error_handler=error_handler)
2224-
2225- self._set_current_page(self.processing_vbox)
2226-
2227- def on_request_password_token_back_button_clicked(self, *args, **kwargs):
2228- """User wants to go to the previous page."""
2229- self._set_current_page(self.login_vbox)
2230-
2231- def on_reset_email_entry_changed(self, widget, *args, **kwargs):
2232- """User is changing the 'widget' entry in the reset email page."""
2233- sensitive = self._non_empty_input(widget)
2234- self.request_password_token_ok_button.set_sensitive(sensitive)
2235-
2236- def on_set_new_password_entries_changed(self, *args, **kwargs):
2237- """User is changing the 'widget' entry in the reset password page."""
2238- sensitive = True
2239- for entry in (self.reset_code_entry,
2240- self.reset_password1_entry,
2241- self.reset_password2_entry):
2242- sensitive &= self._non_empty_input(entry)
2243- self.set_new_password_ok_button.set_sensitive(sensitive)
2244-
2245- def on_set_new_password_ok_button_clicked(self, *args, **kwargs):
2246- """User entered reset code and new passwords."""
2247- if not self.set_new_password_ok_button.is_sensitive():
2248- return
2249-
2250- self._clear_warnings()
2251-
2252- error = False
2253-
2254- token = self.reset_code_entry.get_text()
2255- if not token:
2256- self.reset_code_entry.set_warning(FIELD_REQUIRED)
2257- error = True
2258-
2259- password1 = self.reset_password1_entry.get_text()
2260- password2 = self.reset_password2_entry.get_text()
2261- msg = self._validate_password(password1, password2)
2262- if msg is not None:
2263- self.reset_password1_entry.set_warning(msg)
2264- self.reset_password2_entry.set_warning(msg)
2265- error = True
2266-
2267- if error:
2268- return
2269-
2270- email = self.reset_email_entry.get_text()
2271- logger.info('Calling set_new_password with email %r, token %r and '
2272- 'new password: <hidden>.', email, token)
2273- f = self.backend.set_new_password
2274- error_handler = partial(self._handle_error, f,
2275- self.on_password_change_error)
2276- f(self.app_name, email, token, password1,
2277- reply_handler=NO_OP, error_handler=error_handler)
2278-
2279- self._set_current_page(self.processing_vbox)
2280-
2281- def _webkit_init_ssl(self):
2282- """Set the WebKit ssl strictness."""
2283- # delay the import of webkit to be able to build without it
2284- from gi.repository import WebKit # pylint: disable=E0611
2285-
2286- # Set the Soup session to be strict and use system CA certs
2287- session = WebKit.get_default_session()
2288- session.set_property(STRICT_SSL_PROP, True)
2289- session.set_property(CERTS_FILE_PROP, CA_CERT_FILE)
2290-
2291- def _add_webkit_browser(self):
2292- """Add the webkit browser for the t&c."""
2293- # delay the import of webkit to be able to build without it
2294- from gi.repository import WebKit # pylint: disable=E0611
2295-
2296- self._webkit_init_ssl()
2297-
2298- browser = WebKit.WebView()
2299-
2300- browser.connect('notify::load-status',
2301- self.on_tc_browser_notify_load_status)
2302- browser.connect('navigation-policy-decision-requested',
2303- self.on_tc_browser_navigation_requested)
2304-
2305- settings = browser.get_settings()
2306- settings.set_property("enable-plugins", False)
2307- settings.set_property("enable-default-context-menu", False)
2308-
2309- # webkit_web_view_open has been deprecated since version 1.1.1 and
2310- # should not be used in newly-written code. Use
2311- # webkit_web_view_load_uri() instead.
2312- browser.load_uri(self.tc_url)
2313- browser.show()
2314- self.tc_browser_window.add(browser)
2315-
2316- def on_tc_button_clicked(self, *args, **kwargs):
2317- """The T&C button was clicked, create the browser and load terms."""
2318- if self.tc_browser_window.get_child() is None:
2319- self._add_webkit_browser()
2320- self._set_current_page(self.processing_vbox)
2321- else:
2322- self._set_current_page(self.tc_browser_vbox)
2323-
2324- def on_tc_back_button_clicked(self, *args, **kwargs):
2325- """T & C 'back' button was clicked, return to the previous page."""
2326- self._set_current_page(self.enter_details_vbox)
2327-
2328- def on_tc_browser_notify_load_status(self, browser, *args, **kwargs):
2329- """The T&C page is being loaded."""
2330- from gi.repository import WebKit # pylint: disable=E0611
2331-
2332- if browser.get_load_status().real == WebKit.LoadStatus.FINISHED:
2333- self._set_current_page(self.tc_browser_vbox)
2334-
2335- def on_tc_browser_navigation_requested(self, browser, frame, request,
2336- action, decision, *args, **kwargs):
2337- """The user wants to navigate within the T&C browser."""
2338- from gi.repository import WebKit # pylint: disable=E0611
2339-
2340- if action is not None and \
2341- action.get_reason() == WebKit.WebNavigationReason.LINK_CLICKED:
2342- if decision is not None:
2343- decision.ignore()
2344- url = action.get_original_uri()
2345- webbrowser.open(url)
2346- else:
2347- if decision is not None:
2348- decision.use()
2349-
2350- def on_tc_browser_vbox_hide(self, *args, **kwargs):
2351- """The T&C page is no longer being shown."""
2352- children = self.tc_browser_window.get_children()
2353- if len(children) > 0:
2354- browser = children[0]
2355- self.tc_browser_window.remove(browser)
2356- browser.destroy()
2357- del browser
2358-
2359- def on_captcha_reload_button_clicked(self, *args, **kwargs):
2360- """User clicked the reload captcha button."""
2361- self._generate_captcha()
2362-
2363- # backend callbacks
2364-
2365- def _build_general_error_message(self, errordict):
2366- """Concatenate __all__ and message from the errordict."""
2367- result = None
2368- msg1 = errordict.get('__all__')
2369- msg2 = errordict.get('message')
2370- if msg1 is not None and msg2 is not None:
2371- result = '\n'.join((msg1, msg2))
2372- else:
2373- result = msg1 if msg1 is not None else msg2
2374- return result
2375-
2376- @log_call
2377- def on_captcha_generated(self, app_name, captcha_id, *args, **kwargs):
2378- """Captcha image has been generated and is available to be shown."""
2379- if captcha_id is None:
2380- logger.warning('on_captcha_generated: captcha_id is None for '
2381- 'app_name %r.', app_name)
2382- self._captcha_id = captcha_id
2383- self._set_captcha_image()
2384-
2385- @log_call
2386- def on_captcha_generation_error(self, app_name, error, *args, **kwargs):
2387- """Captcha image generation failed."""
2388- self._set_warning_message(self.warning_label, CAPTCHA_LOAD_ERROR)
2389- self._generate_captcha()
2390-
2391- @log_call
2392- def on_user_registered(self, app_name, email, *args, **kwargs):
2393- """Registration can go on, user needs to verify email."""
2394- help_text = VERIFY_EMAIL_LABEL % {'app_name': self.app_name,
2395- 'email': email}
2396- self.verify_email_vbox.help_text = help_text
2397- self._set_current_page(self.verify_email_vbox)
2398-
2399- @log_call
2400- def on_user_registration_error(self, app_name, error, *args, **kwargs):
2401- """Error in the data provided for registration."""
2402- msg = error.get('email')
2403- if msg is not None:
2404- self.email1_entry.set_warning(msg)
2405- self.email2_entry.set_warning(msg)
2406-
2407- msg = error.get('password')
2408- if msg is not None:
2409- self.password1_entry.set_warning(msg)
2410- self.password2_entry.set_warning(msg)
2411-
2412- msg = self._build_general_error_message(error)
2413- self._generate_captcha()
2414- self._set_current_page(self.enter_details_vbox, warning_text=msg)
2415-
2416- @log_call
2417- def on_email_validated(self, app_name, email, *args, **kwargs):
2418- """User email was successfully verified."""
2419- self.finish_success()
2420-
2421- @log_call
2422- def on_email_validation_error(self, app_name, error, *args, **kwargs):
2423- """User email validation failed."""
2424- msg = error.get('email_token')
2425- if msg is not None:
2426- self.email_token_entry.set_warning(msg)
2427-
2428- msg = self._build_general_error_message(error)
2429- self._set_current_page(self.verify_email_vbox, warning_text=msg)
2430-
2431- @log_call
2432- def on_logged_in(self, app_name, email, *args, **kwargs):
2433- """User was successfully logged in."""
2434- self.finish_success()
2435-
2436- @log_call
2437- def on_login_error(self, app_name, error, *args, **kwargs):
2438- """User was not successfully logged in."""
2439- msg = self._build_general_error_message(error)
2440- self._set_current_page(self.login_vbox, warning_text=msg)
2441-
2442- @log_call
2443- def on_user_not_validated(self, app_name, email, *args, **kwargs):
2444- """User was not validated."""
2445- self.on_user_registered(app_name, email)
2446-
2447- @log_call
2448- def on_password_reset_token_sent(self, app_name, email, *args, **kwargs):
2449- """Password reset token was successfully sent."""
2450- msg = SET_NEW_PASSWORD_LABEL % {'email': email}
2451- self.set_new_password_vbox.help_text = msg
2452- self._set_current_page(self.set_new_password_vbox)
2453-
2454- @log_call
2455- def on_password_reset_error(self, app_name, error, *args, **kwargs):
2456- """Password reset failed."""
2457- msg = self._build_general_error_message(error)
2458- self._set_current_page(self.login_vbox, warning_text=msg)
2459-
2460- @log_call
2461- def on_password_changed(self, app_name, email, *args, **kwargs):
2462- """Password was successfully changed."""
2463- self._set_current_page(self.login_vbox,
2464- warning_text=PASSWORD_CHANGED)
2465-
2466- @log_call
2467- def on_password_change_error(self, app_name, error, *args, **kwargs):
2468- """Password reset failed."""
2469- msg = self._build_general_error_message(error)
2470- self._set_current_page(self.request_password_token_vbox,
2471- warning_text=msg)
2472
2473=== removed file 'ubuntu_sso/gtk/main.py'
2474--- ubuntu_sso/gtk/main.py 2012-04-09 17:38:24 +0000
2475+++ ubuntu_sso/gtk/main.py 1970-01-01 00:00:00 +0000
2476@@ -1,41 +0,0 @@
2477-# -*- coding: utf-8 -*-
2478-#
2479-# Copyright 2012 Canonical Ltd.
2480-#
2481-# This program is free software: you can redistribute it and/or modify it
2482-# under the terms of the GNU General Public License version 3, as published
2483-# by the Free Software Foundation.
2484-#
2485-# This program is distributed in the hope that it will be useful, but
2486-# WITHOUT ANY WARRANTY; without even the implied warranties of
2487-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2488-# PURPOSE. See the GNU General Public License for more details.
2489-#
2490-# You should have received a copy of the GNU General Public License along
2491-# with this program. If not, see <http://www.gnu.org/licenses/>.
2492-#
2493-# In addition, as a special exception, the copyright holders give
2494-# permission to link the code of portions of this program with the
2495-# OpenSSL library under certain conditions as described in each
2496-# individual source file, and distribute linked combinations
2497-# including the two.
2498-# You must obey the GNU General Public License in all respects
2499-# for all of the code used other than OpenSSL. If you modify
2500-# file(s) with this exception, you may extend this exception to your
2501-# version of the file(s), but you are not obligated to do so. If you
2502-# do not wish to do so, delete this exception statement from your
2503-# version. If you delete this exception statement from all source
2504-# files in the program, then also delete it here.
2505-"""Main module to open the GTK UI."""
2506-
2507-# pylint: disable=E0611
2508-from gi.repository import Gtk
2509-# pylint: enable=E0611
2510-
2511-from ubuntu_sso.gtk.gui import UbuntuSSOClientGUI
2512-
2513-
2514-def main(**kwargs):
2515- """Start the GTK mainloop and open the main window."""
2516- UbuntuSSOClientGUI(close_callback=Gtk.main_quit, **kwargs)
2517- Gtk.main()
2518
2519=== removed directory 'ubuntu_sso/gtk/tests'
2520=== removed file 'ubuntu_sso/gtk/tests/__init__.py'
2521--- ubuntu_sso/gtk/tests/__init__.py 2012-04-09 17:38:24 +0000
2522+++ ubuntu_sso/gtk/tests/__init__.py 1970-01-01 00:00:00 +0000
2523@@ -1,29 +0,0 @@
2524-# -*- coding: utf-8 -*-
2525-#
2526-# Copyright 2009-2012 Canonical Ltd.
2527-#
2528-# This program is free software: you can redistribute it and/or modify it
2529-# under the terms of the GNU General Public License version 3, as published
2530-# by the Free Software Foundation.
2531-#
2532-# This program is distributed in the hope that it will be useful, but
2533-# WITHOUT ANY WARRANTY; without even the implied warranties of
2534-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2535-# PURPOSE. See the GNU General Public License for more details.
2536-#
2537-# You should have received a copy of the GNU General Public License along
2538-# with this program. If not, see <http://www.gnu.org/licenses/>.
2539-#
2540-# In addition, as a special exception, the copyright holders give
2541-# permission to link the code of portions of this program with the
2542-# OpenSSL library under certain conditions as described in each
2543-# individual source file, and distribute linked combinations
2544-# including the two.
2545-# You must obey the GNU General Public License in all respects
2546-# for all of the code used other than OpenSSL. If you modify
2547-# file(s) with this exception, you may extend this exception to your
2548-# version of the file(s), but you are not obligated to do so. If you
2549-# do not wish to do so, delete this exception statement from your
2550-# version. If you delete this exception statement from all source
2551-# files in the program, then also delete it here.
2552-"""Tests for the Ubuntu SSO graphical interface."""
2553
2554=== removed file 'ubuntu_sso/gtk/tests/test_gui.py'
2555--- ubuntu_sso/gtk/tests/test_gui.py 2012-06-25 16:02:25 +0000
2556+++ ubuntu_sso/gtk/tests/test_gui.py 1970-01-01 00:00:00 +0000
2557@@ -1,2291 +0,0 @@
2558-# -*- coding: utf-8 -*-
2559-#
2560-# Copyright 2010-2012 Canonical Ltd.
2561-#
2562-# This program is free software: you can redistribute it and/or modify it
2563-# under the terms of the GNU General Public License version 3, as published
2564-# by the Free Software Foundation.
2565-#
2566-# This program is distributed in the hope that it will be useful, but
2567-# WITHOUT ANY WARRANTY; without even the implied warranties of
2568-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2569-# PURPOSE. See the GNU General Public License for more details.
2570-#
2571-# You should have received a copy of the GNU General Public License along
2572-# with this program. If not, see <http://www.gnu.org/licenses/>.
2573-#
2574-# In addition, as a special exception, the copyright holders give
2575-# permission to link the code of portions of this program with the
2576-# OpenSSL library under certain conditions as described in each
2577-# individual source file, and distribute linked combinations
2578-# including the two.
2579-# You must obey the GNU General Public License in all respects
2580-# for all of the code used other than OpenSSL. If you modify
2581-# file(s) with this exception, you may extend this exception to your
2582-# version of the file(s), but you are not obligated to do so. If you
2583-# do not wish to do so, delete this exception statement from your
2584-# version. If you delete this exception statement from all source
2585-# files in the program, then also delete it here.
2586-"""Tests for the GUI for registration/login."""
2587-
2588-import itertools
2589-import logging
2590-import os
2591-
2592-from collections import defaultdict
2593-
2594-# pylint: disable=E0611
2595-from gi.repository import Gdk, Gtk, WebKit
2596-# pylint: enable=E0611
2597-
2598-from twisted.internet import defer
2599-from twisted.trial.unittest import TestCase
2600-from ubuntuone.devtools.handlers import MementoHandler
2601-
2602-from ubuntu_sso.utils.ui import UNKNOWN_ERROR
2603-from ubuntu_sso.gtk import gui
2604-from ubuntu_sso.tests import (APP_NAME, TC_URL, HELP_TEXT, CAPTCHA_ID,
2605- CAPTCHA_SOLUTION, EMAIL, EMAIL_TOKEN, NAME, PASSWORD, PING_URL,
2606- RESET_PASSWORD_TOKEN,
2607-)
2608-
2609-
2610-# Access to a protected member 'yyy' of a client class
2611-# pylint: disable=W0212
2612-
2613-# Instance of 'UbuntuSSOClientGUI' has no 'yyy' member
2614-# pylint: disable=E1101,E1103
2615-
2616-# Use of super on an old style class
2617-# pylint: disable=E1002
2618-
2619-
2620-class FakedSSOBackend(object):
2621- """Fake a SSO Backend."""
2622-
2623- def __init__(self, *args, **kwargs):
2624- self._args = args
2625- self._kwargs = kwargs
2626- self._called = {}
2627- self.callbacks = defaultdict(list)
2628-
2629- for i in ('generate_captcha', 'login', 'login_and_ping',
2630- 'register_user', 'validate_email',
2631- 'validate_email_and_ping',
2632- 'request_password_reset_token',
2633- 'set_new_password'):
2634- setattr(self, i, self._record_call(i))
2635-
2636- def connect_to_signal(self, signal_name, callback):
2637- """Connect a callback to a given signal."""
2638- self.callbacks[signal_name].append(callback)
2639- return callback
2640-
2641- def disconnect_from_signal(self, signal_name, match):
2642- """Disconnect from a given signal."""
2643- self.callbacks[signal_name].remove(match)
2644- if len(self.callbacks[signal_name]) == 0:
2645- self.callbacks.pop(signal_name)
2646-
2647- def _record_call(self, func_name):
2648- """Store values when calling 'func_name'."""
2649-
2650- def inner(*args, **kwargs):
2651- """Fake 'func_name'."""
2652- self._called[func_name] = (args, kwargs)
2653-
2654- return inner
2655-
2656-
2657-class FakedSSOService(object):
2658- """A faked SSO service."""
2659-
2660- def __init__(self):
2661- self.sso_login = FakedSSOBackend()
2662-
2663-
2664-class Settings(dict):
2665- """Faked embedded browser settings."""
2666-
2667- def get_property(self, prop_name):
2668- """Alias for __getitem__."""
2669- return self[prop_name]
2670-
2671- def set_property(self, prop_name, newval):
2672- """Alias for __setitem__."""
2673- self[prop_name] = newval
2674-
2675-
2676-class FakedEmbeddedBrowser(Gtk.TextView):
2677- """Faked an embedded browser."""
2678-
2679- def __init__(self):
2680- super(FakedEmbeddedBrowser, self).__init__()
2681- self._props = {}
2682- self._signals = defaultdict(list)
2683- self._settings = Settings()
2684-
2685- def connect(self, signal_name, callback):
2686- """Connect 'signal_name' with 'callback'."""
2687- self._signals[signal_name].append(callback)
2688-
2689- def load_uri(self, uri):
2690- """Navigate to the given 'uri'."""
2691- self._props['uri'] = uri
2692-
2693- def set_property(self, prop_name, newval):
2694- """Set 'prop_name' to 'newval'."""
2695- self._props[prop_name] = newval
2696-
2697- def get_property(self, prop_name):
2698- """Return the current value for 'prop_name'."""
2699- return self._props[prop_name]
2700-
2701- def get_settings(self,):
2702- """Return the current settings."""
2703- return self._settings
2704-
2705- def get_load_status(self):
2706- """Return the current load status."""
2707- return WebKit.LoadStatus.FINISHED
2708-
2709- def show(self):
2710- """Show this instance."""
2711- self.set_property('visible', True)
2712-
2713-
2714-class BasicTestCase(TestCase):
2715- """Test case with a helper tracker."""
2716-
2717- @defer.inlineCallbacks
2718- def setUp(self):
2719- """Init."""
2720- yield super(BasicTestCase, self).setUp()
2721- self._called = False # helper
2722-
2723- self.patch(gui.sys, 'exit', lambda *a: None)
2724-
2725- self.memento = MementoHandler()
2726- self.memento.setLevel(logging.DEBUG)
2727- gui.logger.addHandler(self.memento)
2728- self.addCleanup(gui.logger.removeHandler, self.memento)
2729-
2730- def _set_called(self, *args, **kwargs):
2731- """Set _called to True."""
2732- self._called = (args, kwargs)
2733-
2734- def assert_color_equal(self, rgba_color, gdk_color):
2735- """Check that 'rgba_color' is the same as 'gdk_color'."""
2736- tmp = Gdk.RGBA()
2737- assert tmp.parse(gdk_color.to_string())
2738-
2739- msg = 'Text color must be %r (got %r instead).'
2740- self.assertEqual(rgba_color, tmp, msg % (rgba_color, tmp))
2741-
2742- def assert_backend_called(self, method, *args, **kwargs):
2743- """Check that 'method(*args, **kwargs)' was called in the backend."""
2744- self.assertIn(method, self.ui.backend._called)
2745-
2746- call = self.ui.backend._called[method]
2747- self.assertEqual(call[0], args)
2748-
2749- reply_handler = call[1].pop('reply_handler')
2750- self.assertEqual(reply_handler, gui.NO_OP)
2751-
2752- error_handler = call[1].pop('error_handler')
2753- self.assertEqual(error_handler.func, self.ui._handle_error)
2754-
2755- self.assertEqual(call[1], kwargs)
2756-
2757-
2758-class LabeledEntryTestCase(BasicTestCase):
2759- """Test suite for the labeled entry."""
2760-
2761- @defer.inlineCallbacks
2762- def setUp(self):
2763- """Init."""
2764- yield super(LabeledEntryTestCase, self).setUp()
2765- self.label = 'Test me please'
2766- self.entry = gui.LabeledEntry(label=self.label)
2767-
2768- # we need a window to be able to realize ourselves
2769- window = Gtk.Window()
2770- window.add(self.entry)
2771- window.show_all()
2772- self.addCleanup(window.hide)
2773- self.addCleanup(window.destroy)
2774-
2775- def grab_focus(self, focus_in=True):
2776- """Grab focus on widget, if None use self.entry."""
2777- direction = 'in' if focus_in else 'out'
2778- self.entry.emit('focus-%s-event' % direction, None)
2779-
2780- # Bad first argument 'LabeledEntry' given to super class
2781- # pylint: disable=E1003
2782- def assert_correct_label(self):
2783- """Check that the entry has the correct label."""
2784- # text content is correct
2785- msg = 'Text content must be %r (got %r instead).'
2786- expected = self.label
2787- actual = super(gui.LabeledEntry, self.entry).get_text()
2788- self.assertEqual(expected, actual, msg % (expected, actual))
2789-
2790- # text color is correct
2791- expected = gui.HELP_TEXT_COLOR
2792- actual = self.entry.get_style().text[Gtk.StateFlags.NORMAL]
2793- self.assert_color_equal(expected, actual)
2794-
2795- def test_initial_text(self):
2796- """Entry have the correct text at startup."""
2797- self.assert_correct_label()
2798-
2799- def test_width_chars(self):
2800- """Entry have the correct width."""
2801- self.assertEqual(self.entry.get_width_chars(), gui.DEFAULT_WIDTH)
2802-
2803- def test_tooltip(self):
2804- """Entry have the correct tooltip."""
2805- msg = 'Tooltip must be %r (got %r instead).'
2806- expected = self.label
2807- actual = self.entry.get_tooltip_text()
2808- # tooltip is correct
2809- self.assertEqual(expected, actual, msg % (expected, actual))
2810-
2811- def test_clear_entry_on_focus_in(self):
2812- """Entry are cleared when focused."""
2813- self.grab_focus()
2814-
2815- msg = 'Entry must be cleared on focus in.'
2816- self.assertEqual('', self.entry.get_text(), msg)
2817-
2818- def test_text_defaults_to_theme_color_when_focus_in(self):
2819- """Entry restore its text color when focused in."""
2820- self.patch(self.entry, 'override_color', self._set_called)
2821-
2822- self.grab_focus()
2823-
2824- self.assertEqual(((Gtk.StateFlags.NORMAL, None), {}), self._called,
2825- 'Entry text color must be restore on focus in.')
2826-
2827- def test_refill_entry_on_focus_out_if_no_input(self):
2828- """Entry is re-filled with label when focused out if no user input."""
2829-
2830- self.grab_focus() # grab focus
2831- self.grab_focus(focus_in=False) # loose focus
2832-
2833- # Entry must be re-filled on focus out
2834- self.assert_correct_label()
2835-
2836- def test_refill_entry_on_focus_out_if_empty_input(self):
2837- """Entry is re-filled with label when focused out if empty input."""
2838-
2839- self.grab_focus() # grab focus
2840-
2841- self.entry.set_text(' ') # add empty text to the entry
2842-
2843- self.grab_focus(focus_in=False) # loose focus
2844-
2845- # Entry must be re-filled on focus out
2846- self.assert_correct_label()
2847-
2848- def test_preserve_input_on_focus_out_if_user_input(self):
2849- """Entry is unmodified when focused out if user input."""
2850- msg = 'Entry must be left unmodified on focus out when user input.'
2851- expected = 'test me please'
2852-
2853- self.grab_focus() # grab focus
2854-
2855- self.entry.set_text(expected) # add empty text to the entry
2856-
2857- self.grab_focus(focus_in=False) # loose focus
2858-
2859- self.assertEqual(expected, self.entry.get_text(), msg)
2860-
2861- def test_preserve_input_on_focus_out_and_in_again(self):
2862- """Entry is unmodified when focused out and then in again."""
2863- msg = 'Entry must be left unmodified on focus out and then in again.'
2864- expected = 'test me I mean it'
2865-
2866- self.grab_focus() # grab focus
2867-
2868- self.entry.set_text(expected) # add text to the entry
2869-
2870- self.grab_focus(focus_in=False) # loose focus
2871- self.grab_focus() # grab focus again!
2872-
2873- self.assertEqual(expected, self.entry.get_text(), msg)
2874-
2875- def test_get_text_ignores_label(self):
2876- """Entry's text is only user input (label is ignored)."""
2877- self.assertEqual(self.entry.get_text(), '')
2878-
2879- def test_get_text_ignores_empty_input(self):
2880- """Entry's text is only user input (empty text is ignored)."""
2881- self.entry.set_text(' ')
2882- self.assertEqual(self.entry.get_text(), '')
2883-
2884- def test_get_text_doesnt_ignore_user_input(self):
2885- """Entry's text is user input."""
2886- self.entry.set_text('a')
2887- self.assertEqual(self.entry.get_text(), 'a')
2888-
2889- def test_no_warning_by_default(self):
2890- """No secondary icon by default."""
2891- self.assertEqual(self.entry.warning, None)
2892- self.assertEqual(self.entry.get_property('secondary-icon-stock'),
2893- None)
2894- self.assertEqual(self.entry.get_property('secondary-icon-sensitive'),
2895- False)
2896- self.assertEqual(self.entry.get_property('secondary-icon-activatable'),
2897- False)
2898- prop = self.entry.get_property('secondary-icon-tooltip-text')
2899- self.assertEqual(prop, None)
2900-
2901- def test_set_warning(self):
2902- """Setting a warning show the proper secondary icon."""
2903- msg = 'You failed!'
2904- self.entry.set_warning(msg)
2905- self.assertEqual(self.entry.warning, msg)
2906- self.assertEqual(self.entry.get_property('secondary-icon-stock'),
2907- Gtk.STOCK_DIALOG_WARNING)
2908- self.assertEqual(self.entry.get_property('secondary-icon-sensitive'),
2909- True)
2910- self.assertEqual(self.entry.get_property('secondary-icon-activatable'),
2911- False)
2912- prop = self.entry.get_property('secondary-icon-tooltip-text')
2913- self.assertEqual(prop, msg)
2914-
2915- def test_clear_warning(self):
2916- """Clearing a warning no longer show the secondary icon."""
2917- self.entry.clear_warning()
2918- self.assertEqual(self.entry.warning, None)
2919- self.assertEqual(self.entry.get_property('secondary-icon-stock'),
2920- None)
2921- self.assertEqual(self.entry.get_property('secondary-icon-sensitive'),
2922- False)
2923- self.assertEqual(self.entry.get_property('secondary-icon-activatable'),
2924- False)
2925- prop = self.entry.get_property('secondary-icon-tooltip-text')
2926- self.assertEqual(prop, None)
2927-
2928-
2929-class PasswordLabeledEntryTestCase(LabeledEntryTestCase):
2930- """Test suite for the labeled entry when is_password is True."""
2931-
2932- @defer.inlineCallbacks
2933- def setUp(self):
2934- """Init."""
2935- yield super(PasswordLabeledEntryTestCase, self).setUp()
2936- self.entry.is_password = True
2937-
2938- def test_password_fields_are_visible_at_startup(self):
2939- """Password entrys show the helping text at startup."""
2940- self.assertTrue(self.entry.get_visibility(),
2941- 'Password entry should be visible at start up.')
2942-
2943- def test_password_field_is_visible_if_no_input_and_focus_out(self):
2944- """Password entry show the label when focus out."""
2945- self.grab_focus() # user cliked or TAB'd to the entry
2946- self.grab_focus(focus_in=False) # loose focus
2947- self.assertTrue(self.entry.get_visibility(),
2948- 'Entry should be visible when focus out and no input.')
2949-
2950- def test_password_fields_are_not_visible_when_editing(self):
2951- """Password entrys show the hidden chars instead of the password."""
2952- self.grab_focus() # user cliked or TAB'd to the entry
2953- self.assertFalse(self.entry.get_visibility(),
2954- 'Entry should not be visible when editing.')
2955-
2956-
2957-class UbuntuSSOClientTestCase(BasicTestCase):
2958- """Basic setup and helper functions."""
2959-
2960- gui_class = gui.UbuntuSSOClientGUI
2961- kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT)
2962-
2963- @defer.inlineCallbacks
2964- def setUp(self):
2965- """Init."""
2966- yield super(UbuntuSSOClientTestCase, self).setUp()
2967- self.patch(gui.main, 'get_sso_client',
2968- lambda: defer.succeed(FakedSSOService()))
2969- self.pages = ('enter_details', 'processing', 'verify_email', 'finish',
2970- 'tc_browser', 'login', 'request_password_token',
2971- 'set_new_password')
2972- self.ui = self.gui_class(**self.kwargs)
2973- self.addCleanup(self.ui.destroy)
2974- self.error = {'message': UNKNOWN_ERROR}
2975-
2976- def assert_entries_are_packed_to_ui(self, container_name, entries):
2977- """Every entry is properly packed in the ui 'container_name'."""
2978- msg = 'Entry %r must be packed in %r but is not.'
2979- container = getattr(self.ui, container_name)
2980- for kind in entries:
2981- name = '%s_entry' % kind
2982- entry = getattr(self.ui, name)
2983- self.assertIsInstance(entry, gui.LabeledEntry)
2984- self.assertIn(entry, container, msg % (name, container_name))
2985-
2986- def assert_warnings_visibility(self, visible=False):
2987- """Every warning label should be 'visible'."""
2988- msg = '%r should have %sempty content.'
2989- for name in self.ui.widgets:
2990- widget = getattr(self.ui, name)
2991- if 'warning' in name:
2992- self.assertEqual('', widget.get_text(),
2993- msg % (name, '' if visible else 'non-'))
2994- elif 'entry' in name:
2995- self.assertEqual(widget.warning, '')
2996-
2997- def assert_correct_label_warning(self, label, message):
2998- """Check that a warning is shown displaying 'message'."""
2999- # warning label is visible
3000- self.assertTrue(label.get_property('visible'))
3001-
3002- # warning content is correct
3003- actual = label.get_text().decode('utf-8')
3004- self.assertEqual(actual, message)
3005-
3006- # content color is correct
3007- # FIXME - New GTK+ 3.5 breaks this check - see bug #1014772
3008- # expected = gui.WARNING_TEXT_COLOR
3009- # actual = label.get_style().fg[Gtk.StateFlags.NORMAL]
3010- # self.assert_color_equal(expected, actual)
3011-
3012- def assert_correct_entry_warning(self, entry, message):
3013- """Check that a warning is shown displaying 'message'."""
3014- self.assertEqual(entry.warning, message)
3015-
3016- def assert_pages_visibility(self, **kwargs):
3017- """The page 'name' is the current page for the content notebook."""
3018- msg = 'page %r must be self.ui.content\'s current page.'
3019- (name, _), = kwargs.items()
3020- page = getattr(self.ui, '%s_vbox' % name)
3021- self.assertEqual(self.ui.content.get_current_page(),
3022- self.ui.content.page_num(page), msg % name)
3023-
3024- def click_join_with_valid_data(self):
3025- """Move to the next page after entering details."""
3026- self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=CAPTCHA_ID)
3027-
3028- self.ui.name_entry.set_text(NAME)
3029- # match emails
3030- self.ui.email1_entry.set_text(EMAIL)
3031- self.ui.email2_entry.set_text(EMAIL)
3032- # match passwords
3033- self.ui.password1_entry.set_text(PASSWORD)
3034- self.ui.password2_entry.set_text(PASSWORD)
3035- if self.ui.tc_url:
3036- # agree to TC, only if the tc_url is defined, so we catch errors
3037- self.ui.yes_to_tc_checkbutton.set_active(True)
3038- # resolve captcha properly
3039- self.ui.captcha_solution_entry.set_text(CAPTCHA_SOLUTION)
3040-
3041- self.ui.join_ok_button.clicked()
3042-
3043- def click_verify_email_with_valid_data(self):
3044- """Move to the next page after entering email token."""
3045- self.click_join_with_valid_data()
3046-
3047- # resolve email token properly
3048- self.ui.email_token_entry.set_text(EMAIL_TOKEN)
3049-
3050- self.ui.verify_token_button.clicked()
3051-
3052- def click_connect_with_valid_data(self):
3053- """Move to the next page after entering login info."""
3054- # enter email
3055- self.ui.login_email_entry.set_text(EMAIL)
3056- # enter password
3057- self.ui.login_password_entry.set_text(PASSWORD)
3058-
3059- self.ui.login_ok_button.clicked()
3060-
3061- def click_request_password_token_with_valid_data(self):
3062- """Move to the next page after requesting for password reset token."""
3063- # enter email
3064- self.ui.reset_email_entry.set_text(EMAIL)
3065-
3066- self.ui.request_password_token_ok_button.clicked()
3067-
3068- def click_set_new_password_with_valid_data(self):
3069- """Move to the next page after resetting password."""
3070- # enter reset code
3071- self.ui.reset_code_entry.set_text(RESET_PASSWORD_TOKEN)
3072- # match passwords
3073- self.ui.reset_password1_entry.set_text(PASSWORD)
3074- self.ui.reset_password2_entry.set_text(PASSWORD)
3075-
3076- self.ui.set_new_password_ok_button.clicked()
3077-
3078-
3079-class BasicUbuntuSSOClientTestCase(UbuntuSSOClientTestCase):
3080- """Test suite for basic functionality."""
3081-
3082- def test_main_window_is_visible_at_startup(self):
3083- """The main window is shown at startup."""
3084- self.assertTrue(self.ui.window.get_property('visible'))
3085-
3086- def test_main_window_is_resizable(self):
3087- """The main window can be resized."""
3088- self.assertTrue(self.ui.window.get_property('resizable'))
3089-
3090- def test_closing_main_window_calls_close_callback(self):
3091- """The close_callback is called when closing the main window."""
3092- self.ui.close_callback = self._set_called
3093- self.ui.on_close_clicked()
3094- self.assertTrue(self._called,
3095- 'close_callback was called when window was closed.')
3096-
3097- def test_close_callback_if_not_set(self):
3098- """The close_callback is a no op if not set."""
3099- self.ui.on_close_clicked()
3100- # no crash when close_callback is not set
3101-
3102- def test_app_name_is_stored(self):
3103- """The app_name is stored for further use."""
3104- self.assertIn(APP_NAME, self.ui.app_name)
3105-
3106- def test_signals_are_removed(self):
3107- """The hooked signals are removed at shutdown time."""
3108- assert len(self.ui.backend.callbacks) > 0 # at least one callback
3109-
3110- self.ui.on_close_clicked()
3111-
3112- self.assertEqual(self.ui.backend.callbacks, {})
3113-
3114- def test_pages_are_packed_into_container(self):
3115- """All the pages are packed in the main container."""
3116- children = self.ui.content.get_children()
3117- for page_name in self.pages:
3118- page = getattr(self.ui, '%s_vbox' % page_name)
3119- self.assertIn(page, children)
3120-
3121- def test_initial_text_for_entries(self):
3122- """Entries have the correct text at startup."""
3123- msg = 'Text for %r must be %r (got %r instead).'
3124- for name in self.ui.entries:
3125- entry = getattr(self.ui, name)
3126- expected = getattr(gui.utils.ui, name.upper())
3127- actual = entry.label
3128- # text content is correct
3129- self.assertEqual(expected, actual, msg % (name, expected, actual))
3130-
3131- def test_entries_activates_default(self):
3132- """Entries have the activates default prop set."""
3133- msg = '%r must have activates_default set to True.'
3134- for name in self.ui.entries:
3135- entry = getattr(self.ui, name)
3136- self.assertTrue(entry.get_activates_default(), msg % (name,))
3137-
3138- def test_password_fields_are_password(self):
3139- """Password fields have the is_password flag set."""
3140- msg = '%r should be a password LabeledEntry instance.'
3141- passwords = filter(lambda name: 'password' in name,
3142- self.ui.entries)
3143- for name in passwords:
3144- widget = getattr(self.ui, name)
3145- self.assertTrue(widget.is_password, msg % name)
3146-
3147- def test_warning_fields_are_cleared(self):
3148- """Every warning label should be cleared."""
3149- self.assert_warnings_visibility()
3150-
3151- def test_cancel_buttons_close_window(self):
3152- """Every cancel button should close the window when clicked."""
3153- self.patch(self.ui.backend, 'disconnect_from_signal', lambda *a: None)
3154- msg = '%r should close the window when clicked.'
3155- buttons = filter(lambda name: 'cancel_button' in name or
3156- 'close_button' in name, self.ui.widgets)
3157- for name in buttons:
3158- self.ui.close_callback = self._set_called
3159- widget = getattr(self.ui, name)
3160- widget.clicked()
3161- self.assertEqual(self._called, ((widget,), {}), msg % name)
3162- self._called = False
3163-
3164- def test_window_icon(self):
3165- """Main window has the proper icon."""
3166- self.assertEqual('ubuntu-logo', self.ui.window.get_icon_name())
3167-
3168- def test_finish_success_shows_success_page(self):
3169- """When calling 'finish_success' the success page is shown."""
3170- self.ui.finish_success()
3171- self.assert_pages_visibility(finish=True)
3172- self.assertEqual(gui.SUCCESS % {'app_name': APP_NAME},
3173- self.ui.finish_vbox.label.get_text().decode('utf8'))
3174- result = self.ui.finish_vbox.label.get_text().decode('utf8')
3175- self.assertTrue(self.ui.app_name in result)
3176-
3177- def test_finish_error_shows_error_page(self):
3178- """When calling 'finish_error' the error page is shown."""
3179- self.ui.finish_error()
3180- self.assert_pages_visibility(finish=True)
3181- self.assertEqual(gui.ERROR,
3182- self.ui.finish_vbox.label.get_text().decode('utf8'))
3183-
3184-
3185-class SetTransientForTestCase(UbuntuSSOClientTestCase):
3186- """Test suite for setting the window as transient for another one."""
3187-
3188- def test_transient_window_is_none_if_window_id_is_zero(self):
3189- """The transient window is correct."""
3190- self.patch(gui.X11Window, 'foreign_new_for_display', self._set_called)
3191- ui = self.gui_class(window_id=0, **self.kwargs)
3192- self.addCleanup(ui.destroy)
3193-
3194- self.assertFalse(self._called, 'set_transient_for must not be called.')
3195-
3196- def test_transient_window_is_correct(self):
3197- """The transient window is correct."""
3198- xid = 5
3199- self.patch(gui.X11Window, 'foreign_new_for_display', self._set_called)
3200- ui = self.gui_class(window_id=xid, **self.kwargs)
3201- self.addCleanup(ui.destroy)
3202-
3203- self.assertTrue(self.memento.check(logging.ERROR, 'set_transient_for'))
3204- self.assertTrue(self.memento.check(logging.ERROR, str(xid)))
3205- self.assertEqual(self._called, ((xid,), {}))
3206-
3207- def test_transient_window_accepts_negative_id(self):
3208- """The transient window accepts a negative window id."""
3209- xid = -5
3210- self.patch(gui.X11Window, 'foreign_new_for_display', self._set_called)
3211- ui = self.gui_class(window_id=xid, **self.kwargs)
3212- self.addCleanup(ui.destroy)
3213-
3214- self.assertEqual(self._called, ((xid,), {}))
3215-
3216-
3217-SetTransientForTestCase.skip = "Apparently, so far we can't use XLib " \
3218-"dynamic bindings to complete the call to X11Window.foreign_new_for_display."
3219-
3220-
3221-class EnterDetailsTestCase(UbuntuSSOClientTestCase):
3222- """Test suite for the user registration (enter details page)."""
3223-
3224- def test_initial_text_for_header_label(self):
3225- """The header must have the correct text at startup."""
3226- msg = 'Text for the header must be %r (got %r instead).'
3227- expected = gui.JOIN_HEADER_LABEL % {'app_name': APP_NAME}
3228- actual = self.ui.header_label.get_text().decode('utf8')
3229- # text content is correct
3230- self.assertEqual(expected, actual, msg % (expected, actual))
3231-
3232- def test_entries_are_packed_to_ui(self):
3233- """Every entry is properly packed in the ui."""
3234- for kind in ('email', 'password'):
3235- container_name = '%ss_hbox' % kind
3236- entries = ('%s%s' % (kind, i) for i in xrange(1, 3))
3237- self.assert_entries_are_packed_to_ui(container_name, entries)
3238-
3239- self.assert_entries_are_packed_to_ui('enter_details_vbox', ('name',))
3240- self.assert_entries_are_packed_to_ui('captcha_solution_vbox',
3241- ('captcha_solution',))
3242- self.assert_entries_are_packed_to_ui('verify_email_details_vbox',
3243- ('email_token',))
3244-
3245- def test_initial_texts_for_checkbuttons(self):
3246- """Check buttons have the correct text at startup."""
3247- msg = 'Text for %r must be %r (got %r instead).'
3248- expected = gui.YES_TO_UPDATES % {'app_name': APP_NAME}
3249- actual = self.ui.yes_to_updates_checkbutton.get_label().decode('utf8')
3250- self.assertEqual(expected, actual, msg % ('yes_to_updates_checkbutton',
3251- expected, actual))
3252- expected = gui.YES_TO_TC % {'app_name': APP_NAME}
3253- actual = self.ui.yes_to_tc_checkbutton.get_label().decode('utf8')
3254- self.assertEqual(expected, actual,
3255- msg % ('yes_to_tc_checkbutton', expected, actual))
3256-
3257- def test_updates_checkbutton_is_checked_at_startup(self):
3258- """The 'yes to updates' checkbutton is checked by default."""
3259- msg = '%r is checked by default.'
3260- name = 'yes_to_updates_checkbutton'
3261- widget = getattr(self.ui, name)
3262- self.assertTrue(widget.get_active(), msg % name)
3263-
3264- def test_tc_checkbutton_is_not_checked_at_startup(self):
3265- """The 'yes to T&C' checkbutton is not checked by default."""
3266- msg = '%r is checked by default.'
3267- name = 'yes_to_tc_checkbutton'
3268- widget = getattr(self.ui, name)
3269- self.assertFalse(widget.get_active(), msg % name)
3270-
3271- def test_vboxes_visible_properties(self):
3272- """Only 'enter_details' vbox is visible at start up."""
3273- self.assert_pages_visibility(enter_details=True)
3274-
3275- def test_join_ok_button_clicked(self):
3276- """Clicking 'join_ok_button' sends info to backend using 'register'."""
3277- self.click_join_with_valid_data()
3278-
3279- # assert register_user was called
3280- self.assert_backend_called('register_user',
3281- APP_NAME, EMAIL, PASSWORD, NAME, CAPTCHA_ID, CAPTCHA_SOLUTION)
3282-
3283- def test_join_ok_button_clicked_morphs_to_processing_page(self):
3284- """Clicking 'join_ok_button' presents the processing vbox."""
3285- self.click_join_with_valid_data()
3286- self.assert_pages_visibility(processing=True)
3287-
3288- def test_processing_vbox_displays_an_active_spinner(self):
3289- """When processing the registration, an active spinner is shown."""
3290- self.click_join_with_valid_data()
3291-
3292- self.assertTrue(self.ui.processing_vbox.get_property('visible'),
3293- 'the processing box should be visible.')
3294-
3295- box = self.ui.processing_vbox.get_children()[0].get_children()[0]
3296- self.assertEqual(2, len(box.get_children()),
3297- 'processing_vbox must have two children.')
3298-
3299- spinner, label = box.get_children()
3300- self.assertIsInstance(spinner, Gtk.Spinner)
3301- self.assertIsInstance(label, Gtk.Label)
3302-
3303- self.assertTrue(spinner.get_property('visible'),
3304- 'the processing spinner should be visible.')
3305- self.assertTrue(spinner.get_property('active'),
3306- 'the processing spinner should be active.')
3307- self.assertTrue(label.get_property('visible'),
3308- 'the processing label should be visible.')
3309- self.assertEqual(label.get_text().decode('utf8'),
3310- gui.ONE_MOMENT_PLEASE,
3311- 'the processing label text must be correct.')
3312-
3313- def test_captcha_image_is_not_visible_at_startup(self):
3314- """Captcha image is not shown at startup."""
3315- self.assertFalse(self.ui.captcha_image.get_property('visible'),
3316- 'the captcha_image should not be visible.')
3317-
3318- def test_captcha_filename_is_different_each_time(self):
3319- """The captcha image is different each time."""
3320- ui = self.gui_class(**self.kwargs)
3321- self.addCleanup(ui.destroy)
3322-
3323- self.assertNotEqual(self.ui._captcha_filename, ui._captcha_filename)
3324-
3325- def test_captcha_image_is_removed_when_exiting(self):
3326- """The captcha image is removed at shutdown time."""
3327- open(self.ui._captcha_filename, 'w').close()
3328- assert os.path.exists(self.ui._captcha_filename)
3329- self.ui.on_close_clicked()
3330-
3331- self.assertFalse(os.path.exists(self.ui._captcha_filename),
3332- 'captcha image must be removed when exiting.')
3333-
3334- def test_captcha_image_is_a_spinner_at_first(self):
3335- """Captcha image shows a spinner until the image is downloaded."""
3336- self.assertTrue(self.ui.captcha_loading.get_property('visible'),
3337- 'the captcha_loading box should be visible.')
3338-
3339- box = self.ui.captcha_loading.get_children()[0].get_children()[0]
3340- self.assertEqual(2, len(box.get_children()),
3341- 'captcha_loading must have two children.')
3342-
3343- spinner, label = box.get_children()
3344- self.assertIsInstance(spinner, Gtk.Spinner)
3345- self.assertIsInstance(label, Gtk.Label)
3346-
3347- self.assertTrue(spinner.get_property('visible'),
3348- 'the captcha_loading spinner should be visible.')
3349- self.assertTrue(spinner.get_property('active'),
3350- 'the captcha_loading spinner should be active.')
3351- self.assertTrue(label.get_property('visible'),
3352- 'the captcha_loading label should be visible.')
3353- self.assertEqual(label.get_text().decode('utf8'), gui.LOADING,
3354- 'the captcha_loading label text must be correct.')
3355-
3356- def test_join_ok_button_is_disabled_until_captcha_is_available(self):
3357- """The join_ok_button is not sensitive until captcha is available."""
3358- self.assertFalse(self.ui.join_ok_button.is_sensitive())
3359-
3360- def test_join_ok_button_is_enabled_when_captcha_is_available(self):
3361- """The join_ok_button is sensitive when captcha is available."""
3362- self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=CAPTCHA_ID)
3363- self.assertTrue(self.ui.join_ok_button.is_sensitive())
3364-
3365- def test_captcha_loading_is_hid_when_captcha_is_available(self):
3366- """The captcha_loading is hid when captcha is available."""
3367- self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=CAPTCHA_ID)
3368- self.assertFalse(self.ui.captcha_loading.get_property('visible'),
3369- 'captcha_loading is not visible.')
3370-
3371- def test_captcha_id_is_stored_when_captcha_is_available(self):
3372- """The captcha_id is stored when captcha is available."""
3373- self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=CAPTCHA_ID)
3374- self.assertEqual(CAPTCHA_ID, self.ui._captcha_id)
3375-
3376- def test_captcha_image_is_requested_as_startup(self):
3377- """The captcha image is requested at startup."""
3378- # assert generate_captcha was called
3379- self.assert_backend_called('generate_captcha',
3380- APP_NAME, self.ui._captcha_filename)
3381-
3382- def test_captcha_is_shown_when_available(self):
3383- """The captcha image is shown when available."""
3384- self.patch(self.ui.captcha_image, 'set_from_file', self._set_called)
3385- self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=CAPTCHA_ID)
3386- self.assertTrue(self.ui.captcha_image.get_property('visible'))
3387- self.assertEqual(self._called, ((self.ui._captcha_filename,), {}))
3388-
3389- def test_on_captcha_generated_logs_captcha_id_when_none(self):
3390- """If the captcha id is None, a warning is logged."""
3391- self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=None)
3392- self.assertTrue(self.memento.check(logging.WARNING, repr(APP_NAME)))
3393- self.assertTrue(self.memento.check(logging.WARNING,
3394- 'captcha_id is None'))
3395-
3396- def test_captcha_reload_button_visible(self):
3397- """The captcha reload button is initially visible."""
3398- self.assertTrue(self.ui.captcha_reload_button.get_visible(),
3399- "The captcha button is not visible")
3400-
3401- def test_captcha_reload_button_reloads_captcha(self):
3402- """The captcha reload button loads a new captcha."""
3403- self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=CAPTCHA_ID)
3404- self.patch(self.ui, '_generate_captcha', self._set_called)
3405- self.ui.captcha_reload_button.clicked()
3406- self.assertEqual(self._called, ((), {}))
3407-
3408- def test_captcha_reload_button_has_tooltip(self):
3409- """The captcha reload button has a tooltip."""
3410- self.assertEqual(self.ui.captcha_reload_button.get_tooltip_text(),
3411- gui.CAPTCHA_RELOAD_TOOLTIP)
3412-
3413- def test_login_button_has_correct_wording(self):
3414- """The sign in button has the proper wording."""
3415- actual = self.ui.login_button.get_label().decode('utf8')
3416- self.assertEqual(gui.LOGIN_BUTTON_LABEL, actual)
3417-
3418- def test_join_ok_button_does_nothing_if_clicked_but_disabled(self):
3419- """The join form can only be submitted if the button is sensitive."""
3420- self.patch(self.ui.email1_entry, 'get_text', self._set_called)
3421-
3422- self.ui.join_ok_button.set_sensitive(False)
3423- self.ui.join_ok_button.clicked()
3424- self.assertFalse(self._called)
3425-
3426- self.ui.join_ok_button.set_sensitive(True)
3427- self.ui.join_ok_button.clicked()
3428- self.assertTrue(self._called)
3429-
3430- def test_user_and_pass_are_cached(self):
3431- """Username and password are temporarly cached for further use."""
3432- self.click_join_with_valid_data()
3433- self.assertEqual(self.ui.user_email, EMAIL)
3434- self.assertEqual(self.ui.user_password, PASSWORD)
3435-
3436- def test_on_captcha_generation_error(self):
3437- """on_captcha_generation_error shows an error and reloads captcha."""
3438- self.patch(self.ui, '_generate_captcha', self._set_called)
3439- self.ui.on_captcha_generation_error(APP_NAME, error=self.error)
3440- self.assert_correct_label_warning(self.ui.warning_label,
3441- gui.CAPTCHA_LOAD_ERROR)
3442- self.assertEqual(self._called, ((), {}))
3443-
3444- def test_captcha_success_after_error(self):
3445- """When captcha was retrieved after error, the warning is removed."""
3446- self.ui.on_captcha_generation_error(APP_NAME, error=self.error)
3447- self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=CAPTCHA_ID)
3448- self.assertEqual(self.ui.warning_label.get_text().decode('utf8'), '')
3449-
3450- def test_has_tc_link(self):
3451- """The T&C button and checkbox are shown if the link is provided"""
3452- self.assertEqual(self.ui.tc_button.get_visible(), True)
3453- self.assertEqual(self.ui.yes_to_tc_checkbutton.get_visible(), True)
3454-
3455-
3456-class NoTermsAndConditionsTestCase(EnterDetailsTestCase):
3457- """Test suite for the user registration (with no t&c link)."""
3458-
3459- kwargs = dict(app_name=APP_NAME, tc_url='', help_text=HELP_TEXT)
3460-
3461- def test_has_tc_link(self):
3462- """The T&C button and checkbox are not shown if no link is provided"""
3463- self.assertEqual(self.ui.tc_vbox.get_visible(), False)
3464-
3465-
3466-class TermsAndConditionsBrowserTestCase(UbuntuSSOClientTestCase):
3467- """Test suite for the terms & conditions browser."""
3468-
3469- @defer.inlineCallbacks
3470- def setUp(self):
3471- yield super(TermsAndConditionsBrowserTestCase, self).setUp()
3472- self.patch(WebKit, 'WebView', FakedEmbeddedBrowser)
3473- self.patch(self.ui, '_webkit_init_ssl', self._set_called)
3474-
3475- self.ui.tc_button.clicked()
3476- self.addCleanup(self.ui.tc_browser_vbox.hide)
3477-
3478- children = self.ui.tc_browser_window.get_children()
3479- assert len(children) == 1
3480- self.browser = children[0]
3481-
3482- def test_ssl_validation(self):
3483- """The browser is set to validate SSL."""
3484- self.assertEqual(self._called, ((), {}),
3485- '_webkit_init_ssl should be called when creating a '
3486- 'webkit browser.')
3487-
3488- def test_tc_browser_is_created_when_tc_page_is_shown(self):
3489- """The browser is created when the TC button is clicked."""
3490- self.ui.on_tc_browser_notify_load_status(self.browser)
3491-
3492- children = self.ui.tc_browser_window.get_children()
3493- self.assertEqual(1, len(children))
3494-
3495- def test_is_visible(self):
3496- """The browser is visible."""
3497- self.assertIsInstance(self.browser, FakedEmbeddedBrowser)
3498- self.assertTrue(self.browser.get_property('visible'))
3499-
3500- def test_settings(self):
3501- """The browser settings are correct."""
3502- settings = self.browser.get_settings()
3503- self.assertFalse(settings.get_property('enable-plugins'))
3504- self.assertFalse(settings.get_property('enable-default-context-menu'))
3505-
3506- def test_tc_browser_is_destroyed_when_tc_page_is_hid(self):
3507- """The browser is destroyed when the TC page is hid."""
3508- self.ui.on_tc_browser_notify_load_status(self.browser)
3509- self.patch(self.browser, 'destroy', self._set_called)
3510- self.ui.tc_browser_vbox.hide()
3511- self.assertEqual(self._called, ((), {}))
3512-
3513- def test_tc_browser_is_removed_when_tc_page_is_hid(self):
3514- """The browser is removed when the TC page is hid."""
3515- self.ui.on_tc_browser_notify_load_status(self.browser)
3516-
3517- self.ui.tc_browser_vbox.hide()
3518-
3519- children = self.ui.tc_browser_window.get_children()
3520- self.assertEqual(0, len(children))
3521-
3522- def test_tc_button_clicked_morphs_into_processing_page(self):
3523- """Clicking the T&C button morphs into processing page."""
3524- self.assert_pages_visibility(processing=True)
3525-
3526- def test_tc_back_clicked_returns_to_previous_page(self):
3527- """Terms & Conditions back button return to previous page."""
3528- self.ui.on_tc_browser_notify_load_status(self.browser)
3529- self.ui.tc_back_button.clicked()
3530- self.assert_pages_visibility(enter_details=True)
3531-
3532- def test_tc_button_has_the_proper_wording(self):
3533- """Terms & Conditions has the proper wording."""
3534- self.assertEqual(self.ui.tc_button.get_label().decode('utf8'),
3535- gui.TC_BUTTON)
3536-
3537- def test_tc_has_no_help_text(self):
3538- """The help text is removed."""
3539- self.ui.on_tc_browser_notify_load_status(self.browser)
3540- self.assertEqual('', self.ui.help_label.get_text().decode('utf8'))
3541-
3542- def test_tc_browser_opens_the_proper_url(self):
3543- """Terms & Conditions browser shows the proper uri."""
3544- self.assertEqual(self.browser.get_property('uri'), TC_URL)
3545-
3546- def test_notify_load_status_connected(self):
3547- """The 'notify::load-status' signal is connected."""
3548- expected = [self.ui.on_tc_browser_notify_load_status]
3549- self.assertEqual(self.browser._signals['notify::load-status'],
3550- expected)
3551-
3552- # Unused variable 'skip'
3553- # pylint: disable=W0612
3554- test_notify_load_status_connected.skip = \
3555- 'Connecting to notify::load-status makes U1 terms navigation fail.'
3556-
3557- def test_notify_load_finished_connected(self):
3558- """The 'load-finished' signal is connected."""
3559- expected = [self.ui.on_tc_browser_notify_load_status]
3560- self.assertEqual(self.browser._signals['notify::load-status'],
3561- expected)
3562-
3563- def test_tc_loaded_morphs_into_tc_browser_vbox(self):
3564- """When the Terms & Conditions is loaded, show the browser window."""
3565- self.ui.on_tc_browser_notify_load_status(self.browser)
3566- self.assert_pages_visibility(tc_browser=True)
3567-
3568- def test_navigation_requested_connected(self):
3569- """The 'navigation-policy-decision-requested' signal is connected."""
3570- actual = self.browser._signals['navigation-policy-decision-requested']
3571- expected = [self.ui.on_tc_browser_navigation_requested]
3572- self.assertEqual(actual, expected)
3573-
3574- def test_navigation_requested_succeeds_for_no_clicking(self):
3575- """The navigation request succeeds when user hasn't clicked a link."""
3576- action = WebKit.WebNavigationAction()
3577- action.set_reason(WebKit.WebNavigationReason.OTHER)
3578-
3579- decision = WebKit.WebPolicyDecision()
3580- decision.use = self._set_called
3581-
3582- kwargs = dict(browser=self.browser, frame=None, request=None,
3583- action=action, decision=decision)
3584- self.ui.on_tc_browser_navigation_requested(**kwargs)
3585- self.assertEqual(self._called, ((), {}))
3586-
3587- def test_navigation_requested_ignores_clicked_links(self):
3588- """The navigation request is ignored if a link was clicked."""
3589- action = WebKit.WebNavigationAction()
3590- action.set_reason(WebKit.WebNavigationReason.LINK_CLICKED)
3591-
3592- decision = WebKit.WebPolicyDecision()
3593- decision.ignore = self._set_called
3594-
3595- self.patch(gui.webbrowser, 'open', lambda *args, **kwargs: None)
3596-
3597- kwargs = dict(browser=self.browser, frame=None, request=None,
3598- action=action, decision=decision)
3599- self.ui.on_tc_browser_navigation_requested(**kwargs)
3600- self.assertEqual(self._called, ((), {}))
3601-
3602- def test_navigation_requested_ignores_for_none(self):
3603- """The navigation request is ignoref the request if params are None."""
3604- kwargs = dict(browser=None, frame=None, request=None,
3605- action=None, decision=None)
3606- self.ui.on_tc_browser_navigation_requested(**kwargs)
3607-
3608- def test_navigation_requested_opens_links_when_clicked(self):
3609- """The navigation request is opened on user's default browser
3610-
3611- (If the user opened a link by clicking into it).
3612-
3613- """
3614- url = 'http://something.com/yadda'
3615- action = WebKit.WebNavigationAction()
3616- action.set_reason(WebKit.WebNavigationReason.LINK_CLICKED)
3617- action.set_original_uri(url)
3618-
3619- decision = WebKit.WebPolicyDecision()
3620- decision.ignore = gui.NO_OP
3621-
3622- self.patch(gui.webbrowser, 'open', self._set_called)
3623-
3624- kwargs = dict(browser=self.browser, frame=None, request=None,
3625- action=action, decision=decision)
3626- self.ui.on_tc_browser_navigation_requested(**kwargs)
3627- self.assertEqual(self._called, ((url,), {}))
3628-
3629- def test_on_tc_button_clicked_no_child(self):
3630- """Test the tc loading with no child."""
3631- called = []
3632-
3633- def fake_add_browser():
3634- """Fake add browser."""
3635- called.append('fake_add_browser')
3636-
3637- self.patch(self.ui, '_add_webkit_browser', fake_add_browser)
3638- self.patch(self.ui.tc_browser_window, 'get_child', lambda: None)
3639-
3640- self.ui.on_tc_button_clicked()
3641- self.assertIn('fake_add_browser', called)
3642-
3643- def test_on_tc_button_clicked_child(self):
3644- """Test the tc loading with child."""
3645- called = []
3646-
3647- def fake_add_browser(i_self):
3648- """Fake add browser."""
3649- called.append('fake_add_browser')
3650-
3651- self.patch(self.ui, '_add_webkit_browser', fake_add_browser)
3652-
3653- browser = WebKit.WebView()
3654- self.ui.tc_browser_window.add(browser)
3655- self.ui.on_tc_button_clicked()
3656- self.assertNotIn('fake_add_browser', called)
3657-
3658-
3659-class RegistrationErrorTestCase(UbuntuSSOClientTestCase):
3660- """Test suite for the user registration error handling."""
3661-
3662- @defer.inlineCallbacks
3663- def setUp(self):
3664- """Init."""
3665- yield super(RegistrationErrorTestCase, self).setUp()
3666- self.click_join_with_valid_data()
3667-
3668- def test_previous_page_is_shown(self):
3669- """On UserRegistrationError the previous page is shown."""
3670- self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
3671- self.assert_pages_visibility(enter_details=True)
3672-
3673- def test_captcha_is_reloaded(self):
3674- """On UserRegistrationError the captcha is reloaded."""
3675- self.patch(self.ui, '_generate_captcha', self._set_called)
3676- self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
3677- self.assertEqual(self._called, ((), {}))
3678-
3679- def test_warning_label_is_shown(self):
3680- """On UserRegistrationError the warning label is shown."""
3681- self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
3682- self.assert_correct_label_warning(self.ui.warning_label,
3683- UNKNOWN_ERROR)
3684-
3685- def test_specific_errors_from_backend_are_shown(self):
3686- """Specific errors from backend are used."""
3687- error = {'errtype': 'RegistrationError',
3688- 'message': 'We\'re so doomed.',
3689- 'email': 'Enter a valid e-mail address.',
3690- 'password': 'I don\'t like your password.',
3691- '__all__': 'Wrong captcha solution.'}
3692-
3693- self.ui.on_user_registration_error(app_name=APP_NAME, error=error)
3694-
3695- expected = '\n'.join((error['__all__'], error['message']))
3696- self.assert_correct_label_warning(self.ui.warning_label, expected)
3697- self.assert_correct_entry_warning(self.ui.email1_entry,
3698- error['email'])
3699- self.assert_correct_entry_warning(self.ui.email2_entry,
3700- error['email'])
3701- self.assert_correct_entry_warning(self.ui.password1_entry,
3702- error['password'])
3703- self.assert_correct_entry_warning(self.ui.password2_entry,
3704- error['password'])
3705-
3706-
3707-class VerifyEmailTestCase(UbuntuSSOClientTestCase):
3708- """Test suite for the user registration (verify email page)."""
3709-
3710- method = 'validate_email'
3711- method_args = (APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN)
3712-
3713- @defer.inlineCallbacks
3714- def setUp(self):
3715- """Init."""
3716- yield super(VerifyEmailTestCase, self).setUp()
3717- self.ui.on_user_registered(app_name=APP_NAME, email=EMAIL)
3718-
3719- def test_registration_successful_shows_verify_email_vbox(self):
3720- """Receiving 'registration_successful' shows the verify email vbox."""
3721- self.ui.on_user_registered(app_name=APP_NAME, email=EMAIL)
3722- self.assert_pages_visibility(verify_email=True)
3723-
3724- def test_help_label_display_correct_wording(self):
3725- """The help_label display VERIFY_EMAIL_LABEL."""
3726- msg = 'help_label must read %r (got %r instead).'
3727- actual = self.ui.help_label.get_label().decode('utf8')
3728- expected = gui.VERIFY_EMAIL_LABEL % {'app_name': APP_NAME,
3729- 'email': EMAIL}
3730- self.assertEqual(expected, actual, msg % (expected, actual))
3731-
3732- def test_on_verify_token_button_clicked_calls_backend(self):
3733- """Verify token button triggers call to backend."""
3734- self.click_verify_email_with_valid_data()
3735- self.assert_backend_called(self.method, *self.method_args)
3736-
3737- def test_on_verify_token_button_clicked(self):
3738- """Verify token uses cached user_email and user_password."""
3739- self.ui.user_email = 'test@me.com'
3740- self.ui.user_password = 'yadda-yedda'
3741- method_args = list(self.method_args)
3742- method_args[1] = self.ui.user_email
3743- method_args[2] = self.ui.user_password
3744-
3745- # resolve email token properly
3746- self.ui.email_token_entry.set_text(EMAIL_TOKEN)
3747-
3748- self.ui.on_verify_token_button_clicked()
3749- self.assert_backend_called(self.method, *tuple(method_args))
3750-
3751- def test_on_verify_token_button_shows_processing_page(self):
3752- """Verify token button triggers call to backend."""
3753- self.click_verify_email_with_valid_data()
3754- self.assert_pages_visibility(processing=True)
3755-
3756- def test_no_warning_messages_if_valid_data(self):
3757- """No warning messages are shown if the data is valid."""
3758- # this will certainly NOT generate warnings
3759- self.click_verify_email_with_valid_data()
3760- self.assert_warnings_visibility()
3761-
3762- def test_on_email_validated_shows_finish_page(self):
3763- """On email validated the finish page is shown."""
3764- self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
3765- self.assert_pages_visibility(finish=True)
3766-
3767- def test_on_email_validated_does_not_clear_the_help_text(self):
3768- """On email validated the help text is not removed."""
3769- self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
3770- self.assertEqual(self.ui.verify_email_vbox.help_text,
3771- self.ui.help_label.get_label().decode('utf8'))
3772-
3773- def test_on_email_validation_error_verify_email_is_shown(self):
3774- """On email validation error, the verify_email page is shown."""
3775- self.ui.on_email_validation_error(app_name=APP_NAME, error=self.error)
3776- self.assert_pages_visibility(verify_email=True)
3777- self.assert_correct_label_warning(self.ui.warning_label,
3778- UNKNOWN_ERROR)
3779-
3780- def test_specific_errors_from_backend_are_shown(self):
3781- """Specific errors from backend are used."""
3782- error = {'errtype': 'EmailValidationError',
3783- 'message': 'We\'re so doomed.',
3784- 'email_token': 'Enter a valid e-mail address.',
3785- '__all__': 'We all are gonna die.'}
3786-
3787- self.ui.on_email_validation_error(app_name=APP_NAME, error=error)
3788-
3789- expected = '\n'.join((error['__all__'], error['message']))
3790- self.assert_correct_label_warning(self.ui.warning_label, expected)
3791- self.assert_correct_entry_warning(self.ui.email_token_entry,
3792- error['email_token'])
3793-
3794- def test_success_label_is_correct(self):
3795- """The success message is correct."""
3796- self.assertEqual(gui.SUCCESS % {'app_name': APP_NAME},
3797- self.ui.success_vbox.label.get_text().decode('utf8'))
3798- markup = self.ui.success_vbox.label.get_label().decode('utf8')
3799- self.assertTrue('<span size="x-large">' in markup)
3800- self.assertTrue(self.ui.app_name in markup)
3801-
3802- def test_error_label_is_correct(self):
3803- """The error message is correct."""
3804- self.assertEqual(gui.ERROR,
3805- self.ui.error_vbox.label.get_text().decode('utf8'))
3806- markup = self.ui.error_vbox.label.get_label().decode('utf8')
3807- self.assertTrue('<span size="x-large">' in markup)
3808-
3809- def test_on_finish_close_button_clicked_closes_window(self):
3810- """When done the window is closed."""
3811- self.ui.finish_close_button.clicked()
3812- self.assertFalse(self.ui.window.get_property('visible'))
3813-
3814- def test_verify_token_button_does_nothing_if_clicked_but_disabled(self):
3815- """The email token can only be submitted if the button is sensitive."""
3816- self.patch(self.ui.email_token_entry, 'get_text', self._set_called)
3817-
3818- self.ui.verify_token_button.set_sensitive(False)
3819- self.ui.verify_token_button.clicked()
3820- self.assertFalse(self._called)
3821-
3822- self.ui.verify_token_button.set_sensitive(True)
3823- self.ui.verify_token_button.clicked()
3824- self.assertTrue(self._called)
3825-
3826- def test_after_email_validated_finish_success(self):
3827- """After email_validated is called, finish_success is called."""
3828- self.patch(self.ui, 'finish_success', self._set_called)
3829-
3830- self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
3831-
3832- self.assertEqual(self._called, ((), {}))
3833-
3834-
3835-class VerifyEmailWithPingTestCase(VerifyEmailTestCase):
3836- """Test suite for the user registration (verify email page)."""
3837-
3838- kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT,
3839- ping_url=PING_URL)
3840- method = 'validate_email_and_ping'
3841- method_args = (APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN, PING_URL)
3842-
3843-
3844-class VerifyEmailValidationTestCase(UbuntuSSOClientTestCase):
3845- """Test suite for the user registration validation (verify email page)."""
3846-
3847- @defer.inlineCallbacks
3848- def setUp(self):
3849- """Init."""
3850- yield super(VerifyEmailValidationTestCase, self).setUp()
3851- self.ui.on_user_registered(app_name=APP_NAME, email=EMAIL)
3852-
3853- def test_warning_is_shown_if_empty_email_token(self):
3854- """A warning message is shown if email token is empty."""
3855- self.ui.email_token_entry.set_text('')
3856-
3857- self.ui.verify_token_button.clicked()
3858-
3859- self.assert_correct_entry_warning(self.ui.email_token_entry,
3860- gui.FIELD_REQUIRED)
3861- self.assertNotIn('validate_email', self.ui.backend._called)
3862-
3863- def test_no_warning_messages_if_valid_data(self):
3864- """No warning messages are shown if the data is valid."""
3865- # this will certainly NOT generate warnings
3866- self.click_verify_email_with_valid_data()
3867-
3868- self.assert_warnings_visibility()
3869-
3870- def test_no_warning_messages_if_valid_data_after_invalid_data(self):
3871- """No warnings if the data is valid (with prior invalid data)."""
3872- # this will certainly generate warnings
3873- self.ui.verify_token_button.clicked()
3874-
3875- # this will certainly NOT generate warnings
3876- self.click_verify_email_with_valid_data()
3877-
3878- self.assert_warnings_visibility()
3879-
3880-
3881-class VerifyEmailLoginOnlyTestCase(VerifyEmailTestCase):
3882- """Test suite for the user login (verify email page)."""
3883-
3884- kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT,
3885- login_only=True)
3886-
3887-
3888-class VerifyEmailValidationLoginOnlyTestCase(VerifyEmailValidationTestCase):
3889- """Test suite for the user login validation (verify email page)."""
3890-
3891- kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT,
3892- login_only=True)
3893-
3894-
3895-class RegistrationValidationTestCase(UbuntuSSOClientTestCase):
3896- """Test suite for the user registration validations."""
3897-
3898- @defer.inlineCallbacks
3899- def setUp(self):
3900- """Init."""
3901- yield super(RegistrationValidationTestCase, self).setUp()
3902- self.ui.join_ok_button.set_sensitive(True)
3903-
3904- def test_warning_is_shown_if_name_empty(self):
3905- """A warning message is shown if name is empty."""
3906- self.ui.name_entry.set_text('')
3907-
3908- self.ui.join_ok_button.clicked()
3909-
3910- self.assert_correct_entry_warning(self.ui.name_entry,
3911- gui.FIELD_REQUIRED)
3912- self.assertNotIn('register_user', self.ui.backend._called)
3913-
3914- def test_warning_is_shown_if_empty_email(self):
3915- """A warning message is shown if emails are empty."""
3916- self.ui.email1_entry.set_text('')
3917- self.ui.email2_entry.set_text('')
3918-
3919- self.ui.join_ok_button.clicked()
3920-
3921- self.assert_correct_entry_warning(self.ui.email1_entry,
3922- gui.FIELD_REQUIRED)
3923- self.assert_correct_entry_warning(self.ui.email2_entry,
3924- gui.FIELD_REQUIRED)
3925- self.assertNotIn('register_user', self.ui.backend._called)
3926-
3927- def test_warning_is_shown_if_email_mismatch(self):
3928- """A warning message is shown if emails doesn't match."""
3929- self.ui.email1_entry.set_text(EMAIL)
3930- self.ui.email2_entry.set_text(EMAIL * 2)
3931-
3932- self.ui.join_ok_button.clicked()
3933-
3934- self.assert_correct_entry_warning(self.ui.email1_entry,
3935- gui.EMAIL_MISMATCH)
3936- self.assert_correct_entry_warning(self.ui.email2_entry,
3937- gui.EMAIL_MISMATCH)
3938- self.assertNotIn('register_user', self.ui.backend._called)
3939-
3940- def test_warning_is_shown_if_invalid_email(self):
3941- """A warning message is shown if email is invalid."""
3942- self.ui.email1_entry.set_text('q')
3943- self.ui.email2_entry.set_text('q')
3944-
3945- self.ui.join_ok_button.clicked()
3946-
3947- self.assert_correct_entry_warning(self.ui.email1_entry,
3948- gui.EMAIL_INVALID)
3949- self.assert_correct_entry_warning(self.ui.email2_entry,
3950- gui.EMAIL_INVALID)
3951- self.assertNotIn('register_user', self.ui.backend._called)
3952-
3953- def test_password_help_is_always_shown(self):
3954- """Password help text is correctly displayed."""
3955- self.assertTrue(self.ui.password_help_label.get_property('visible'),
3956- 'password help text is visible.')
3957- self.assertEqual(self.ui.password_help_label.get_text().decode('utf8'),
3958- gui.PASSWORD_HELP)
3959- self.assertNotIn('register_user', self.ui.backend._called)
3960-
3961- def test_warning_is_shown_if_password_mismatch(self):
3962- """A warning message is shown if password doesn't match."""
3963- self.ui.password1_entry.set_text(PASSWORD)
3964- self.ui.password2_entry.set_text(PASSWORD * 2)
3965-
3966- self.ui.join_ok_button.clicked()
3967-
3968- self.assert_correct_entry_warning(self.ui.password1_entry,
3969- gui.PASSWORD_MISMATCH)
3970- self.assert_correct_entry_warning(self.ui.password2_entry,
3971- gui.PASSWORD_MISMATCH)
3972- self.assertNotIn('register_user', self.ui.backend._called)
3973-
3974- def test_warning_is_shown_if_password_too_weak(self):
3975- """A warning message is shown if password is too weak."""
3976- # password will match but will be too weak
3977- for pwd in ('', 'h3lloWo', PASSWORD.lower(), 'helloWorld'):
3978- self.ui.password1_entry.set_text(pwd)
3979- self.ui.password2_entry.set_text(pwd)
3980-
3981- self.ui.join_ok_button.clicked()
3982-
3983- self.assert_correct_entry_warning(self.ui.password1_entry,
3984- gui.PASSWORD_TOO_WEAK)
3985- self.assert_correct_entry_warning(self.ui.password2_entry,
3986- gui.PASSWORD_TOO_WEAK)
3987- self.assertNotIn('register_user', self.ui.backend._called)
3988-
3989- def test_warning_is_shown_if_tc_not_accepted(self):
3990- """A warning message is shown if TC are not accepted."""
3991- # don't agree to TC
3992- self.ui.yes_to_tc_checkbutton.set_active(False)
3993-
3994- self.ui.join_ok_button.clicked()
3995-
3996- self.assert_correct_label_warning(self.ui.tc_warning_label,
3997- gui.TC_NOT_ACCEPTED % {'app_name': APP_NAME})
3998- self.assertNotIn('register_user', self.ui.backend._called)
3999-
4000- def test_warning_is_shown_if_not_captcha_solution(self):
4001- """A warning message is shown if TC are not accepted."""
4002- # captcha solution will be empty
4003- self.ui.captcha_solution_entry.set_text('')
4004-
4005- self.ui.join_ok_button.clicked()
4006-
4007- self.assert_correct_entry_warning(self.ui.captcha_solution_entry,
4008- gui.FIELD_REQUIRED)
4009- self.assertNotIn('register_user', self.ui.backend._called)
4010-
4011- def test_no_warning_messages_if_valid_data(self):
4012- """No warning messages are shown if the data is valid."""
4013- # this will certainly NOT generate warnings
4014- self.click_join_with_valid_data()
4015-
4016- self.assert_warnings_visibility()
4017-
4018- def test_no_warning_messages_if_valid_data_after_invalid_data(self):
4019- """No warnings if the data is valid (with prior invalid data)."""
4020- # this will certainly generate warnings
4021- self.ui.join_ok_button.clicked()
4022-
4023- # this will certainly NOT generate warnings
4024- self.click_join_with_valid_data()
4025-
4026- self.assert_warnings_visibility()
4027-
4028-
4029-class LoginTestCase(UbuntuSSOClientTestCase):
4030- """Test suite for the user login pages."""
4031-
4032- method = 'login'
4033- method_args = (APP_NAME, EMAIL, PASSWORD)
4034-
4035- @defer.inlineCallbacks
4036- def setUp(self):
4037- """Init."""
4038- yield super(LoginTestCase, self).setUp()
4039- self.ui.login_button.clicked()
4040-
4041- def test_login_button_clicked_morphs_to_login_page(self):
4042- """Clicking sig_in_button morphs window into login page."""
4043- self.assert_pages_visibility(login=True)
4044-
4045- def test_initial_text_for_header_label(self):
4046- """The header must have the correct text when logging in."""
4047- msg = 'Text for the header must be %r (got %r instead).'
4048- expected = gui.LOGIN_HEADER_LABEL % {'app_name': APP_NAME}
4049- actual = self.ui.header_label.get_text().decode('utf8')
4050- self.assertEqual(expected, actual, msg % (expected, actual))
4051-
4052- def test_initial_text_for_help_label(self):
4053- """The help must have the correct text at startup."""
4054- msg = 'Text for the help must be %r (got %r instead).'
4055- expected = gui.CONNECT_HELP_LABEL % {'app_name': APP_NAME}
4056- actual = self.ui.help_label.get_text().decode('utf8')
4057- self.assertEqual(expected, actual, msg % (expected, actual))
4058-
4059- def test_entries_are_packed_to_ui_for_login(self):
4060- """Every entry is properly packed in the ui for the login page."""
4061- entries = ('login_email', 'login_password')
4062- self.assert_entries_are_packed_to_ui('login_details_vbox', entries)
4063-
4064- def test_entries_are_packed_to_ui_for_set_new_password(self):
4065- """Every entry is packed in the ui for the reset password page."""
4066- entries = ('reset_code', 'reset_password1', 'reset_password2')
4067- self.assert_entries_are_packed_to_ui('set_new_password_details_vbox',
4068- entries)
4069-
4070- def test_entries_are_packed_to_ui_for_request_password_token(self):
4071- """Every entry is packed in the ui for the reset email page."""
4072- container_name = 'request_password_token_details_vbox'
4073- entries = ('reset_email',)
4074- self.assert_entries_are_packed_to_ui(container_name, entries)
4075-
4076- def test_on_login_back_button_clicked(self):
4077- """Clicking login_back_button show registration page."""
4078- self.ui.login_back_button.clicked()
4079- self.assert_pages_visibility(enter_details=True)
4080-
4081- def test_on_login_connect_button_clicked(self):
4082- """Clicking login_ok_button calls backend.login."""
4083- self.click_connect_with_valid_data()
4084- self.assert_backend_called(self.method, *self.method_args)
4085-
4086- def test_on_login_connect_button_clicked_morphs_to_processing_page(self):
4087- """Clicking login_ok_button morphs to the processing page."""
4088- self.click_connect_with_valid_data()
4089- self.assert_pages_visibility(processing=True)
4090-
4091- def test_on_logged_in_morphs_to_finish_page(self):
4092- """When user logged in the finish page is shown."""
4093- self.click_connect_with_valid_data()
4094- self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
4095- self.assert_pages_visibility(finish=True)
4096-
4097- def test_on_login_error_morphs_to_login_page(self):
4098- """On user login error, the previous page is shown."""
4099- self.click_connect_with_valid_data()
4100- self.ui.on_login_error(app_name=APP_NAME, error=self.error)
4101- self.assert_pages_visibility(login=True)
4102-
4103- def test_on_user_not_validated_morphs_to_verify_page(self):
4104- """On user not validated, the verify page is shown."""
4105- self.click_connect_with_valid_data()
4106- self.ui.on_user_not_validated(app_name=APP_NAME, email=EMAIL)
4107- self.assert_pages_visibility(verify_email=True)
4108-
4109- def test_on_login_error_a_warning_is_shown(self):
4110- """On user login error, a warning is shown with proper wording."""
4111- self.click_connect_with_valid_data()
4112- self.ui.on_login_error(app_name=APP_NAME, error=self.error)
4113- self.assert_correct_label_warning(self.ui.warning_label,
4114- UNKNOWN_ERROR)
4115-
4116- def test_specific_errors_from_backend_are_shown(self):
4117- """Specific errors from backend are used."""
4118- error = {'errtype': 'AuthenticationError',
4119- 'message': 'We\'re so doomed.',
4120- '__all__': 'We all are gonna die.'}
4121-
4122- self.ui.on_login_error(app_name=APP_NAME, error=error)
4123-
4124- expected = '\n'.join((error['__all__'], error['message']))
4125- self.assert_correct_label_warning(self.ui.warning_label, expected)
4126-
4127- def test_back_to_registration_hides_warning(self):
4128- """After user login error, warning is hidden when clicking 'Back'."""
4129- self.click_connect_with_valid_data()
4130- self.ui.on_login_error(app_name=APP_NAME, error=self.error)
4131- self.ui.login_back_button.clicked()
4132- self.assert_warnings_visibility()
4133-
4134- def test_login_ok_button_does_nothing_if_clicked_but_disabled(self):
4135- """The join form can only be submitted if the button is sensitive."""
4136- self.patch(self.ui.login_email_entry, 'get_text', self._set_called)
4137-
4138- self.ui.login_ok_button.set_sensitive(False)
4139- self.ui.login_ok_button.clicked()
4140- self.assertFalse(self._called)
4141-
4142- self.ui.login_ok_button.set_sensitive(True)
4143- self.ui.login_ok_button.clicked()
4144- self.assertTrue(self._called)
4145-
4146- def test_user_and_pass_are_cached(self):
4147- """Username and password are temporarly cached for further use."""
4148- self.click_connect_with_valid_data()
4149- self.assertEqual(self.ui.user_email, EMAIL)
4150- self.assertEqual(self.ui.user_password, PASSWORD)
4151-
4152- def test_after_login_success_finish_success(self):
4153- """After logged_in is called, finish_success is called."""
4154- self.patch(self.ui, 'finish_success', self._set_called)
4155-
4156- self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
4157-
4158- self.assertEqual(self._called, ((), {}))
4159-
4160-
4161-class LoginWithPingTestCase(LoginTestCase):
4162- """Test suite for the login when the ping_url is set."""
4163-
4164- kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT,
4165- ping_url=PING_URL)
4166- method = 'login_and_ping'
4167- method_args = (APP_NAME, EMAIL, PASSWORD, PING_URL)
4168-
4169-
4170-class LoginValidationTestCase(UbuntuSSOClientTestCase):
4171- """Test suite for the user login validation."""
4172-
4173- @defer.inlineCallbacks
4174- def setUp(self):
4175- """Init."""
4176- yield super(LoginValidationTestCase, self).setUp()
4177- self.ui.login_button.clicked()
4178-
4179- def test_warning_is_shown_if_empty_email(self):
4180- """A warning message is shown if email is empty."""
4181- self.ui.login_email_entry.set_text('')
4182-
4183- self.ui.login_ok_button.clicked()
4184-
4185- self.assert_correct_entry_warning(self.ui.login_email_entry,
4186- gui.FIELD_REQUIRED)
4187- self.assertNotIn('login', self.ui.backend._called)
4188-
4189- def test_warning_is_shown_if_invalid_email(self):
4190- """A warning message is shown if email is invalid."""
4191- self.ui.login_email_entry.set_text('q')
4192-
4193- self.ui.login_ok_button.clicked()
4194-
4195- self.assert_correct_entry_warning(self.ui.login_email_entry,
4196- gui.EMAIL_INVALID)
4197- self.assertNotIn('login', self.ui.backend._called)
4198-
4199- def test_warning_is_shown_if_empty_password(self):
4200- """A warning message is shown if password is empty."""
4201- self.ui.login_password_entry.set_text('')
4202-
4203- self.ui.login_ok_button.clicked()
4204-
4205- self.assert_correct_entry_warning(self.ui.login_password_entry,
4206- gui.FIELD_REQUIRED)
4207- self.assertNotIn('login', self.ui.backend._called)
4208-
4209- def test_no_warning_messages_if_valid_data(self):
4210- """No warning messages are shown if the data is valid."""
4211- # this will certainly NOT generate warnings
4212- self.click_connect_with_valid_data()
4213-
4214- self.assert_warnings_visibility()
4215-
4216- def test_no_warning_messages_if_valid_data_after_invalid_data(self):
4217- """No warnings if the data is valid (with prior invalid data)."""
4218- # this will certainly generate warnings
4219- self.ui.login_ok_button.clicked()
4220-
4221- # this will certainly NOT generate warnings
4222- self.click_connect_with_valid_data()
4223-
4224- self.assert_warnings_visibility()
4225-
4226-
4227-class ResetPasswordTestCase(UbuntuSSOClientTestCase):
4228- """Test suite for the reset password functionality."""
4229-
4230- @defer.inlineCallbacks
4231- def setUp(self):
4232- """Init."""
4233- yield super(ResetPasswordTestCase, self).setUp()
4234- self.ui.login_button.clicked()
4235- self.ui.forgotten_password_button.clicked()
4236-
4237- def test_forgotten_password_button_has_the_proper_wording(self):
4238- """The forgotten_password_button has the proper wording."""
4239- actual = self.ui.forgotten_password_button.get_label()
4240- self.assertEqual(actual.decode('utf8'), gui.FORGOTTEN_PASSWORD_BUTTON)
4241-
4242- def test_on_forgotten_password_button_clicked_help_text(self):
4243- """Clicking forgotten_password_button the help is properly changed."""
4244- wanted = gui.REQUEST_PASSWORD_TOKEN_LABEL % {'app_name': APP_NAME}
4245- self.assertEqual(self.ui.help_label.get_text().decode('utf8'), wanted)
4246-
4247- def test_on_forgotten_password_button_clicked_header_label(self):
4248- """Clicking forgotten_password_button the title is properly changed."""
4249- self.assertEqual(self.ui.header_label.get_text().decode('utf8'),
4250- gui.RESET_PASSWORD)
4251-
4252- def test_on_forgotten_password_button_clicked_ok_button(self):
4253- """Clicking forgotten_password_button the ok button reads 'Next'."""
4254- actual = self.ui.request_password_token_ok_button.get_label()
4255- self.assertEqual(actual.decode('utf8'), gui.NEXT)
4256-
4257- def test_on_forgotten_password_button_clicked_morphs_window(self):
4258- """Clicking forgotten_password_button the proper page is shown."""
4259- self.assert_pages_visibility(request_password_token=True)
4260-
4261- def test_on_request_password_token_back_button_clicked(self):
4262- """Clicking request_password_token_back_button show login screen."""
4263- self.ui.request_password_token_back_button.clicked()
4264- self.assert_pages_visibility(login=True)
4265-
4266- def test_request_password_token_ok_button_disabled_until_email_added(self):
4267- """The button is disabled until email added."""
4268- is_sensitive = self.ui.request_password_token_ok_button.get_sensitive
4269- self.assertFalse(is_sensitive())
4270-
4271- self.ui.reset_email_entry.set_text('a')
4272- self.assertTrue(is_sensitive())
4273-
4274- self.ui.reset_email_entry.set_text('')
4275- self.assertFalse(is_sensitive())
4276-
4277- self.ui.reset_email_entry.set_text(' ')
4278- self.assertFalse(is_sensitive())
4279-
4280- def test_on_request_password_token_ok_button_clicked_morphs_window(self):
4281- """Clicking request_password_token_ok_button morphs processing page."""
4282- self.click_request_password_token_with_valid_data()
4283- self.assert_pages_visibility(processing=True)
4284-
4285- def test_on_request_password_token_ok_button_clicked_calls_backend(self):
4286- """Clicking request_password_token_ok_button the backend is called."""
4287- self.click_request_password_token_with_valid_data()
4288- self.assert_backend_called('request_password_reset_token',
4289- APP_NAME, EMAIL)
4290-
4291- def test_on_password_reset_token_sent_morphs_window(self):
4292- """When the reset token was sent, the reset password page is shown."""
4293- self.click_request_password_token_with_valid_data()
4294- self.ui.on_password_reset_token_sent(app_name=APP_NAME, email=EMAIL)
4295- self.assert_pages_visibility(set_new_password=True)
4296-
4297- def test_on_password_reset_token_sent_help_text(self):
4298- """Clicking request_password_token_ok_button changes the help text."""
4299- self.click_request_password_token_with_valid_data()
4300- self.ui.on_password_reset_token_sent(app_name=APP_NAME, email=EMAIL)
4301-
4302- self.assertEqual(self.ui.help_label.get_text().decode('utf8'),
4303- gui.SET_NEW_PASSWORD_LABEL % {'email': EMAIL})
4304-
4305- def test_on_password_reset_token_sent_ok_button(self):
4306- """After request_password_token_ok_button the ok button is updated."""
4307- self.click_request_password_token_with_valid_data()
4308- self.ui.on_password_reset_token_sent(app_name=APP_NAME, email=EMAIL)
4309-
4310- actual = self.ui.set_new_password_ok_button.get_label()
4311- self.assertEqual(actual.decode('utf8'), gui.RESET_PASSWORD)
4312-
4313- def test_on_password_reset_error_shows_login_page(self):
4314- """When reset token wasn't successfuly sent the login page is shown."""
4315- self.ui.on_password_reset_error(app_name=APP_NAME, error=self.error)
4316- self.assert_correct_label_warning(self.ui.warning_label,
4317- UNKNOWN_ERROR)
4318- self.assert_pages_visibility(login=True)
4319-
4320- def test_specific_errors_from_backend_are_shown(self):
4321- """Specific errors from backend are used."""
4322- error = {'errtype': 'ResetPasswordTokenError',
4323- 'message': 'We\'re so doomed.',
4324- '__all__': 'We all are gonna die.'}
4325-
4326- self.ui.on_password_reset_error(app_name=APP_NAME, error=error)
4327-
4328- expected = '\n'.join((error['__all__'], error['message']))
4329- self.assert_correct_label_warning(self.ui.warning_label, expected)
4330-
4331- def test_ok_button_does_nothing_if_clicked_but_disabled(self):
4332- """The password token can be requested if the button is sensitive."""
4333- self.patch(self.ui.reset_email_entry, 'get_text', self._set_called)
4334-
4335- self.ui.request_password_token_ok_button.set_sensitive(False)
4336- self.ui.request_password_token_ok_button.clicked()
4337- self.assertFalse(self._called)
4338-
4339- self.ui.request_password_token_ok_button.set_sensitive(True)
4340- self.ui.request_password_token_ok_button.clicked()
4341- self.assertTrue(self._called)
4342-
4343-
4344-class ResetPasswordValidationTestCase(UbuntuSSOClientTestCase):
4345- """Test suite for the password reset validations."""
4346-
4347- def test_warning_is_shown_if_empty_email(self):
4348- """A warning message is shown if emails are empty."""
4349- self.ui.reset_email_entry.set_text(' ')
4350-
4351- self.ui.request_password_token_ok_button.set_sensitive(True)
4352- self.ui.request_password_token_ok_button.clicked()
4353-
4354- self.assert_correct_entry_warning(self.ui.reset_email_entry,
4355- gui.FIELD_REQUIRED)
4356- self.assertNotIn('request_password_reset_token',
4357- self.ui.backend._called)
4358-
4359- def test_warning_is_shown_if_invalid_email(self):
4360- """A warning message is shown if email is invalid."""
4361- self.ui.reset_email_entry.set_text('q')
4362-
4363- self.ui.request_password_token_ok_button.clicked()
4364-
4365- self.assert_correct_entry_warning(self.ui.reset_email_entry,
4366- gui.EMAIL_INVALID)
4367- self.assertNotIn('request_password_reset_token',
4368- self.ui.backend._called)
4369-
4370- def test_no_warning_messages_if_valid_data(self):
4371- """No warning messages are shown if the data is valid."""
4372- # this will certainly NOT generate warnings
4373- self.click_request_password_token_with_valid_data()
4374-
4375- self.assert_warnings_visibility()
4376-
4377- def test_no_warning_messages_if_valid_data_after_invalid_data(self):
4378- """No warnings if the data is valid (with prior invalid data)."""
4379- # this will certainly generate warnings
4380- self.ui.request_password_token_ok_button.clicked()
4381-
4382- # this will certainly NOT generate warnings
4383- self.click_request_password_token_with_valid_data()
4384-
4385- self.assert_warnings_visibility()
4386-
4387-
4388-class SetNewPasswordTestCase(UbuntuSSOClientTestCase):
4389- """Test suite for setting a new password functionality."""
4390-
4391- @defer.inlineCallbacks
4392- def setUp(self):
4393- """Init."""
4394- yield super(SetNewPasswordTestCase, self).setUp()
4395- self.click_request_password_token_with_valid_data()
4396- self.ui.on_password_reset_token_sent(app_name=APP_NAME, email=EMAIL)
4397-
4398- def test_on_set_new_password_ok_button_disabled(self):
4399- """The set_new_password_ok_button is disabled until values added."""
4400- self.click_request_password_token_with_valid_data()
4401- self.assertFalse(self.ui.set_new_password_ok_button.get_sensitive())
4402-
4403- msg = 'set_new_password_ok_button must be sensitive (%s) for %r.'
4404- entries = (self.ui.reset_code_entry,
4405- self.ui.reset_password1_entry,
4406- self.ui.reset_password2_entry)
4407- for values in itertools.product(('', ' ', 'a'), repeat=3):
4408- expected = True
4409- for entry, val in zip(entries, values):
4410- entry.set_text(val)
4411- expected &= bool(val and not val.isspace())
4412-
4413- actual = self.ui.set_new_password_ok_button.get_sensitive()
4414- self.assertEqual(expected, actual, msg % (expected, values))
4415-
4416- def test_on_set_new_password_ok_button_clicked_morphs_window(self):
4417- """Clicking set_new_password_ok_button the processing page is shown."""
4418- self.click_set_new_password_with_valid_data()
4419- self.assert_pages_visibility(processing=True)
4420-
4421- def test_on_set_new_password_ok_button_clicked_calls_backend(self):
4422- """Clicking set_new_password_ok_button the backend is called."""
4423- self.click_set_new_password_with_valid_data()
4424- self.assert_backend_called('set_new_password',
4425- APP_NAME, EMAIL, RESET_PASSWORD_TOKEN, PASSWORD)
4426-
4427- def test_on_password_changed_shows_login_page(self):
4428- """When password was successfuly changed the login page is shown."""
4429- self.ui.on_password_changed(app_name=APP_NAME, email=EMAIL)
4430- self.assert_correct_label_warning(self.ui.warning_label,
4431- gui.PASSWORD_CHANGED)
4432- self.assert_pages_visibility(login=True)
4433-
4434- def test_on_password_change_error_shows_login_page(self):
4435- """When password wasn't changed the reset password page is shown."""
4436- self.ui.on_password_change_error(app_name=APP_NAME, error=self.error)
4437- self.assert_correct_label_warning(self.ui.warning_label,
4438- UNKNOWN_ERROR)
4439- self.assert_pages_visibility(request_password_token=True)
4440-
4441- def test_specific_errors_from_backend_are_shown(self):
4442- """Specific errors from backend are used."""
4443- error = {'errtype': 'NewPasswordError',
4444- 'message': 'We\'re so doomed.',
4445- '__all__': 'We all are gonna die.'}
4446-
4447- self.ui.on_password_change_error(app_name=APP_NAME, error=error)
4448-
4449- expected = '\n'.join((error['__all__'], error['message']))
4450- self.assert_correct_label_warning(self.ui.warning_label, expected)
4451-
4452- def test_ok_button_does_nothing_if_clicked_but_disabled(self):
4453- """The new passwrd can only be set if the button is sensitive."""
4454- self.patch(self.ui.reset_code_entry, 'get_text', self._set_called)
4455-
4456- self.ui.set_new_password_ok_button.set_sensitive(False)
4457- self.ui.set_new_password_ok_button.clicked()
4458- self.assertFalse(self._called)
4459-
4460- self.ui.set_new_password_ok_button.set_sensitive(True)
4461- self.ui.set_new_password_ok_button.clicked()
4462- self.assertTrue(self._called)
4463-
4464-
4465-class SetNewPasswordValidationTestCase(UbuntuSSOClientTestCase):
4466- """Test suite for validations for setting a new password."""
4467-
4468- def test_warning_is_shown_if_reset_code_empty(self):
4469- """A warning message is shown if reset_code is empty."""
4470- self.ui.reset_code_entry.set_text('')
4471-
4472- self.ui.set_new_password_ok_button.set_sensitive(True)
4473- self.ui.set_new_password_ok_button.clicked()
4474-
4475- self.assert_correct_entry_warning(self.ui.reset_code_entry,
4476- gui.FIELD_REQUIRED)
4477- self.assertNotIn('set_new_password', self.ui.backend._called)
4478-
4479- def test_password_help_is_always_shown(self):
4480- """Password help text is correctly displayed."""
4481- visible = self.ui.reset_password_help_label.get_property('visible')
4482- self.assertTrue(visible, 'password help text is visible.')
4483- actual = self.ui.reset_password_help_label.get_text()
4484- self.assertEqual(actual.decode('utf8'), gui.PASSWORD_HELP)
4485- self.assertNotIn('set_new_password', self.ui.backend._called)
4486-
4487- def test_warning_is_shown_if_password_mismatch(self):
4488- """A warning message is shown if password doesn't match."""
4489- self.ui.reset_password1_entry.set_text(PASSWORD)
4490- self.ui.reset_password2_entry.set_text(PASSWORD * 2)
4491-
4492- self.ui.set_new_password_ok_button.set_sensitive(True)
4493- self.ui.set_new_password_ok_button.clicked()
4494-
4495- self.assert_correct_entry_warning(self.ui.reset_password1_entry,
4496- gui.PASSWORD_MISMATCH)
4497- self.assert_correct_entry_warning(self.ui.reset_password2_entry,
4498- gui.PASSWORD_MISMATCH)
4499- self.assertNotIn('set_new_password', self.ui.backend._called)
4500-
4501- def test_warning_is_shown_if_password_too_weak(self):
4502- """A warning message is shown if password is too weak."""
4503- # password will match but will be too weak
4504- for pwd in ('', 'h3lloWo', PASSWORD.lower(), 'helloWorld'):
4505- self.ui.reset_password1_entry.set_text(pwd)
4506- self.ui.reset_password2_entry.set_text(pwd)
4507-
4508- self.ui.set_new_password_ok_button.set_sensitive(True)
4509- self.ui.set_new_password_ok_button.clicked()
4510-
4511- self.assert_correct_entry_warning(self.ui.reset_password1_entry,
4512- gui.PASSWORD_TOO_WEAK)
4513- self.assert_correct_entry_warning(self.ui.reset_password2_entry,
4514- gui.PASSWORD_TOO_WEAK)
4515- self.assertNotIn('set_new_password', self.ui.backend._called)
4516-
4517- def test_no_warning_messages_if_valid_data(self):
4518- """No warning messages are shown if the data is valid."""
4519- # this will certainly NOT generate warnings
4520- self.click_set_new_password_with_valid_data()
4521-
4522- self.assert_warnings_visibility()
4523-
4524- def test_no_warning_messages_if_valid_data_after_invalid_data(self):
4525- """No warnings if the data is valid (with prior invalid data)."""
4526- # this will certainly generate warnings
4527- self.ui.set_new_password_ok_button.clicked()
4528-
4529- # this will certainly NOT generate warnings
4530- self.click_set_new_password_with_valid_data()
4531-
4532- self.assert_warnings_visibility()
4533-
4534-
4535-class SignalsTestCase(UbuntuSSOClientTestCase):
4536- """Test suite for the backend signals."""
4537-
4538- def test_all_the_signals_are_listed(self):
4539- """All the backend signals are listed to be binded."""
4540- for sig in ('CaptchaGenerated', 'CaptchaGenerationError',
4541- 'UserRegistered', 'UserRegistrationError',
4542- 'LoggedIn', 'LoginError', 'UserNotValidated',
4543- 'EmailValidated', 'EmailValidationError',
4544- 'PasswordResetTokenSent', 'PasswordResetError',
4545- 'PasswordChanged', 'PasswordChangeError'):
4546- self.assertIn(sig, self.ui._signals)
4547-
4548- def test_signal_receivers_are_connected(self):
4549- """Callbacks are connected to signals of interest."""
4550- msg1 = 'callback %r for signal %r must be added to the backend.'
4551- msg2 = 'callback %r for signal %r must be added to the ui log.'
4552- for signal, method in self.ui._signals.items():
4553- actual = self.ui.backend.callbacks.get(signal)
4554- self.assertEqual([method], actual, msg1 % (method, signal))
4555- actual = self.ui._signals_receivers.get(signal)
4556- self.assertEqual(method, actual, msg2 % (method, signal))
4557-
4558- def test_callbacks_only_log_when_app_name_doesnt_match(self):
4559- """Callbacks do nothing but logging when app_name doesn't match."""
4560- mismatch_app_name = self.ui.app_name * 2
4561- for method in self.ui._signals.values():
4562- msgs = ('ignoring', method.__name__, repr(mismatch_app_name))
4563- method(mismatch_app_name, 'dummy')
4564- self.assertTrue(self.memento.check(logging.INFO, *msgs))
4565- self.memento.records = []
4566-
4567- def test_on_captcha_generated_is_not_called(self):
4568- """on_captcha_generated is not called if incorrect app_name."""
4569- self.patch(self.ui, 'on_captcha_generated', self._set_called)
4570- mismatch_app_name = self.ui.app_name * 2
4571- self.ui._signals['CaptchaGenerated'](mismatch_app_name, 'dummy')
4572- self.assertFalse(self._called)
4573-
4574- def test_on_captcha_generation_error_is_not_called(self):
4575- """on_captcha_generation_error is not called if incorrect app_name."""
4576- self.patch(self.ui, 'on_captcha_generation_error', self._set_called)
4577- mismatch_app_name = self.ui.app_name * 2
4578- self.ui._signals['CaptchaGenerationError'](mismatch_app_name, 'dummy')
4579- self.assertFalse(self._called)
4580-
4581- def test_on_user_registered_is_not_called(self):
4582- """on_user_registered is not called if incorrect app_name."""
4583- self.patch(self.ui, 'on_user_registered', self._set_called)
4584- mismatch_app_name = self.ui.app_name * 2
4585- self.ui._signals['UserRegistered'](mismatch_app_name, 'dummy')
4586- self.assertFalse(self._called)
4587-
4588- def test_on_user_registration_error_is_not_called(self):
4589- """on_user_registration_error is not called if incorrect app_name."""
4590- self.patch(self.ui, 'on_user_registration_error', self._set_called)
4591- mismatch_app_name = self.ui.app_name * 2
4592- self.ui._signals['UserRegistrationError'](mismatch_app_name, 'dummy')
4593- self.assertFalse(self._called)
4594-
4595- def test_on_email_validated_is_not_called(self):
4596- """on_email_validated is not called if incorrect app_name."""
4597- self.patch(self.ui, 'on_email_validated', self._set_called)
4598- mismatch_app_name = self.ui.app_name * 2
4599- self.ui._signals['EmailValidated'](mismatch_app_name, 'dummy')
4600- self.assertFalse(self._called)
4601-
4602- def test_on_email_validation_error_is_not_called(self):
4603- """on_email_validation_error is not called if incorrect app_name."""
4604- self.patch(self.ui, 'on_email_validation_error', self._set_called)
4605- mismatch_app_name = self.ui.app_name * 2
4606- self.ui._signals['EmailValidationError'](mismatch_app_name, 'dummy')
4607- self.assertFalse(self._called)
4608-
4609- def test_on_logged_in_is_not_called(self):
4610- """on_logged_in is not called if incorrect app_name."""
4611- self.patch(self.ui, 'on_logged_in', self._set_called)
4612- mismatch_app_name = self.ui.app_name * 2
4613- self.ui._signals['LoggedIn'](mismatch_app_name, 'dummy')
4614- self.assertFalse(self._called)
4615-
4616- def test_on_login_error_is_not_called(self):
4617- """on_login_error is not called if incorrect app_name."""
4618- self.patch(self.ui, 'on_login_error', self._set_called)
4619- mismatch_app_name = self.ui.app_name * 2
4620- self.ui._signals['LoginError'](mismatch_app_name, 'dummy')
4621- self.assertFalse(self._called)
4622-
4623- def test_on_user_not_validated_is_not_called(self):
4624- """on_user_not_validated is not called if incorrect app_name."""
4625- self.patch(self.ui, 'on_user_not_validated', self._set_called)
4626- mismatch_app_name = self.ui.app_name * 2
4627- self.ui._signals['UserNotValidated'](mismatch_app_name, 'dummy')
4628- self.assertFalse(self._called)
4629-
4630- def test_on_password_reset_token_sent_is_not_called(self):
4631- """on_password_reset_token_sent is not called if incorrect app_name."""
4632- self.patch(self.ui, 'on_password_reset_token_sent', self._set_called)
4633- mismatch_app_name = self.ui.app_name * 2
4634- self.ui._signals['PasswordResetTokenSent'](mismatch_app_name, 'dummy')
4635- self.assertFalse(self._called)
4636-
4637- def test_on_password_reset_error_is_not_called(self):
4638- """on_password_reset_error is not called if incorrect app_name."""
4639- self.patch(self.ui, 'on_password_reset_error', self._set_called)
4640- mismatch_app_name = self.ui.app_name * 2
4641- self.ui._signals['PasswordResetError'](mismatch_app_name, 'dummy')
4642- self.assertFalse(self._called)
4643-
4644- def test_on_password_changed_is_not_called(self):
4645- """on_password_changed is not called if incorrect app_name."""
4646- self.patch(self.ui, 'on_password_changed', self._set_called)
4647- mismatch_app_name = self.ui.app_name * 2
4648- self.ui._signals['PasswordChanged'](mismatch_app_name, 'dummy')
4649- self.assertFalse(self._called)
4650-
4651- def test_on_password_change_error_is_not_called(self):
4652- """on_password_change_error is not called if incorrect app_name."""
4653- self.patch(self.ui, 'on_password_change_error', self._set_called)
4654- mismatch_app_name = self.ui.app_name * 2
4655- self.ui._signals['PasswordChangeError'](mismatch_app_name, 'dummy')
4656- self.assertFalse(self._called)
4657-
4658-
4659-class LoginOnlyTestCase(UbuntuSSOClientTestCase):
4660- """Test suite for the login only GUI."""
4661-
4662- kwargs = dict(app_name=APP_NAME, tc_url=None, help_text=HELP_TEXT,
4663- login_only=True)
4664-
4665- def test_login_is_first_page(self):
4666- """When starting, the login page is the first one."""
4667- self.assert_pages_visibility(login=True)
4668-
4669- def test_no_back_button(self):
4670- """There is no back button in the login screen."""
4671- self.assertFalse(self.ui.login_back_button.get_property('visible'))
4672-
4673- def test_login_ok_button_has_the_focus(self):
4674- """The login_ok_button has the focus."""
4675- self.assertTrue(self.ui.login_ok_button.is_focus())
4676-
4677- def test_help_text_is_used(self):
4678- """The passed help_text is used."""
4679- self.assertEqual(self.ui.help_label.get_text().decode('utf8'),
4680- HELP_TEXT)
4681-
4682-
4683-class ReturnCodeTestCase(UbuntuSSOClientTestCase):
4684- """Test the return codes."""
4685-
4686- @defer.inlineCallbacks
4687- def setUp(self):
4688- yield super(ReturnCodeTestCase, self).setUp()
4689- self.patch(gui.sys, 'exit', self._set_called)
4690-
4691- def test_closing_main_window(self):
4692- """When closing the main window, USER_CANCELLATION is called."""
4693- self.ui.window.emit('delete-event', Gdk.Event())
4694- self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}))
4695-
4696- def test_every_cancel_calls_proper_callback(self):
4697- """When any cancel button is clicked, USER_CANCELLATION is called."""
4698- self.patch(self.ui.backend, 'disconnect_from_signal', lambda *a: None)
4699- msg = 'USER_CANCELLATION should be returned when %r is clicked.'
4700- buttons = filter(lambda name: 'cancel_button' in name, self.ui.widgets)
4701- for name in buttons:
4702- widget = getattr(self.ui, name)
4703- widget.clicked()
4704- self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}),
4705- msg % name)
4706- self._called = False
4707-
4708- def test_on_user_registration_error_proper_callback_is_called(self):
4709- """On UserRegistrationError, USER_CANCELLATION is called."""
4710- self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
4711- self.ui.on_close_clicked()
4712-
4713- self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}))
4714-
4715- def test_on_email_validated_proper_callback_is_called(self):
4716- """On EmailValidated, REGISTRATION_SUCCESS is called."""
4717- self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
4718- self.ui.on_close_clicked()
4719-
4720- self.assertEqual(self._called, ((gui.USER_SUCCESS,), {}))
4721-
4722- def test_on_email_validation_error_proper_callback_is_called(self):
4723- """On EmailValidationError, USER_CANCELLATION is called."""
4724- self.ui.on_email_validation_error(app_name=APP_NAME, error=self.error)
4725- self.ui.on_close_clicked()
4726-
4727- self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}))
4728-
4729- def test_on_logged_in_proper_callback_is_called(self):
4730- """On LoggedIn, LOGIN_SUCCESS is called."""
4731- self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
4732- self.ui.on_close_clicked()
4733-
4734- self.assertEqual(self._called, ((gui.USER_SUCCESS,), {}))
4735-
4736- def test_on_login_error_proper_callback_is_called(self):
4737- """On LoginError, USER_CANCELLATION is called."""
4738- self.click_connect_with_valid_data()
4739- self.ui.on_login_error(app_name=APP_NAME, error=self.error)
4740- self.ui.on_close_clicked()
4741-
4742- self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}))
4743-
4744- def test_registration_success_even_if_prior_registration_error(self):
4745- """Only one callback is called with the final outcome.
4746-
4747- When the user successfully registers, REGISTRATION_SUCCESS is
4748- called even if there were errors before.
4749-
4750- """
4751- self.click_join_with_valid_data()
4752- self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
4753- self.click_join_with_valid_data()
4754- self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
4755- self.ui.on_close_clicked()
4756-
4757- self.assertEqual(self._called, ((gui.USER_SUCCESS,), {}))
4758-
4759- def test_login_success_even_if_prior_login_error(self):
4760- """Only one callback is called with the final outcome.
4761-
4762- When the user successfully logins, LOGIN_SUCCESS is called even if
4763- there were errors before.
4764-
4765- """
4766- self.click_connect_with_valid_data()
4767- self.ui.on_login_error(app_name=APP_NAME, error=self.error)
4768- self.click_connect_with_valid_data()
4769- self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
4770- self.ui.on_close_clicked()
4771-
4772- self.assertEqual(self._called, ((gui.USER_SUCCESS,), {}))
4773-
4774- def test_user_cancelation_even_if_prior_registration_error(self):
4775- """Only one callback is called with the final outcome.
4776-
4777- When the user closes the window, USER_CANCELLATION is called even if
4778- there were registration errors before.
4779-
4780- """
4781- self.click_join_with_valid_data()
4782- self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
4783- self.ui.join_cancel_button.clicked()
4784-
4785- self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}))
4786-
4787- def test_user_cancelation_even_if_prior_login_error(self):
4788- """Only one callback is called with the final outcome.
4789-
4790- When the user closes the window, USER_CANCELLATION is called even if
4791- there were login errors before.
4792-
4793- """
4794- self.click_connect_with_valid_data()
4795- self.ui.on_login_error(app_name=APP_NAME, error=self.error)
4796- self.ui.login_cancel_button.clicked()
4797-
4798- self.assertEqual(self._called, ((gui.USER_CANCELLATION,), {}))
4799-
4800-
4801-class DefaultButtonsTestCase(UbuntuSSOClientTestCase):
4802- """Each UI page has a default button when visible."""
4803-
4804- @defer.inlineCallbacks
4805- def setUp(self):
4806- """Init."""
4807- yield super(DefaultButtonsTestCase, self).setUp()
4808- self.mapping = (
4809- ('enter_details_vbox', 'join_ok_button'),
4810- ('tc_browser_vbox', 'tc_back_button'),
4811- ('verify_email_vbox', 'verify_token_button'),
4812- ('login_vbox', 'login_ok_button'),
4813- ('request_password_token_vbox',
4814- 'request_password_token_ok_button'),
4815- ('set_new_password_vbox', 'set_new_password_ok_button'),
4816- ('success_vbox', 'finish_close_button'),
4817- ('error_vbox', 'finish_close_button'),
4818- ('processing_vbox', None))
4819-
4820- def test_pages_have_default_widget_set(self):
4821- """Each page has a proper button as default widget."""
4822- msg = 'Page %r must have %r as default_widget (got %r instead).'
4823- for pname, bname in self.mapping:
4824- page = getattr(self.ui, pname)
4825- button = bname and getattr(self.ui, bname)
4826- self.assertTrue(page.default_widget is button,
4827- msg % (pname, bname, page.default_widget))
4828-
4829- def test_default_widget_can_default(self):
4830- """Each default button can default."""
4831- msg = 'Button %r must have can-default enabled.'
4832- for _, bname in self.mapping:
4833- if bname is not None:
4834- button = getattr(self.ui, bname)
4835- self.assertTrue(button.get_property('can-default'),
4836- msg % bname)
4837-
4838- def test_set_current_page_grabs_focus_for_default_button(self):
4839- """Setting the current page marks the default widget as default."""
4840- msg = '%r "has_default" must be True when %r if the current page.'
4841- for pname, bname in self.mapping:
4842- if bname is not None:
4843- page = getattr(self.ui, pname)
4844- self.patch(page.default_widget, 'grab_default',
4845- self._set_called)
4846- self.ui._set_current_page(page)
4847- self.assertEqual(self._called, ((), {}), msg % (bname, pname))
4848- self._called = False
4849
4850=== removed file 'ubuntu_sso/gtk/tests/test_main.py'
4851--- ubuntu_sso/gtk/tests/test_main.py 2012-04-09 17:38:24 +0000
4852+++ ubuntu_sso/gtk/tests/test_main.py 1970-01-01 00:00:00 +0000
4853@@ -1,51 +0,0 @@
4854-# -*- coding: utf-8 -*-
4855-#
4856-# Copyright 2012 Canonical Ltd.
4857-#
4858-# This program is free software: you can redistribute it and/or modify it
4859-# under the terms of the GNU General Public License version 3, as published
4860-# by the Free Software Foundation.
4861-#
4862-# This program is distributed in the hope that it will be useful, but
4863-# WITHOUT ANY WARRANTY; without even the implied warranties of
4864-# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4865-# PURPOSE. See the GNU General Public License for more details.
4866-#
4867-# You should have received a copy of the GNU General Public License along
4868-# with this program. If not, see <http://www.gnu.org/licenses/>.
4869-#
4870-# In addition, as a special exception, the copyright holders give
4871-# permission to link the code of portions of this program with the
4872-# OpenSSL library under certain conditions as described in each
4873-# individual source file, and distribute linked combinations
4874-# including the two.
4875-# You must obey the GNU General Public License in all respects
4876-# for all of the code used other than OpenSSL. If you modify
4877-# file(s) with this exception, you may extend this exception to your
4878-# version of the file(s), but you are not obligated to do so. If you
4879-# do not wish to do so, delete this exception statement from your
4880-# version. If you delete this exception statement from all source
4881-# files in the program, then also delete it here.
4882-"""Tests for the main module."""
4883-
4884-from twisted.trial.unittest import TestCase
4885-
4886-from ubuntu_sso.gtk import main
4887-
4888-
4889-class BasicTestCase(TestCase):
4890- """Test case with a helper tracker."""
4891-
4892- def test_main(self):
4893- """Calling main.main() a UI instance is created."""
4894- called = []
4895- self.patch(main, 'UbuntuSSOClientGUI',
4896- lambda **kw: called.append(('GUI', kw)))
4897- self.patch(main.Gtk, 'main',
4898- lambda: called.append('main'))
4899-
4900- kwargs = dict(foo='foo', bar='bar', baz='yadda', yadda=0)
4901- main.main(**kwargs)
4902-
4903- kwargs['close_callback'] = main.Gtk.main_quit
4904- self.assertEqual(called, [('GUI', kwargs), 'main'])
4905
4906=== modified file 'ubuntu_sso/keyring/__init__.py'
4907--- ubuntu_sso/keyring/__init__.py 2012-04-19 19:29:15 +0000
4908+++ ubuntu_sso/keyring/__init__.py 2012-07-18 18:54:28 +0000
4909@@ -30,7 +30,13 @@
4910
4911 import socket
4912 import sys
4913-import urllib
4914+
4915+try:
4916+ # pylint: disable=E0611,F0401
4917+ from urllib.parse import quote
4918+ # pylint: enable=E0611,F0401
4919+except ImportError:
4920+ from urllib import quote
4921
4922 from twisted.internet.defer import inlineCallbacks, returnValue
4923
4924@@ -58,9 +64,9 @@
4925
4926 def get_old_token_name(app_name):
4927 """Build the token name (old style). Return an unicode."""
4928- quoted_app_name = urllib.quote(app_name)
4929+ quoted_app_name = quote(app_name)
4930 computer_name = gethostname()
4931- quoted_computer_name = urllib.quote(computer_name)
4932+ quoted_computer_name = quote(computer_name)
4933
4934 assert isinstance(computer_name, unicode)
4935 assert isinstance(quoted_computer_name, unicode)
4936
4937=== modified file 'ubuntu_sso/keyring/linux.py'
4938--- ubuntu_sso/keyring/linux.py 2012-04-19 19:29:15 +0000
4939+++ ubuntu_sso/keyring/linux.py 2012-07-18 18:54:28 +0000
4940@@ -29,8 +29,13 @@
4941 # files in the program, then also delete it here.
4942 """Handle keys in the local kerying."""
4943
4944-import urllib
4945-import urlparse
4946+try:
4947+ # pylint: disable=E0611,F0401
4948+ from urllib.parse import parse_qsl, urlencode
4949+ # pylint: enable=E0611,F0401
4950+except ImportError:
4951+ from urllib import urlencode
4952+ from urlparse import parse_qsl
4953
4954 from twisted.internet.defer import inlineCallbacks, returnValue
4955
4956@@ -78,7 +83,7 @@
4957 def set_credentials(self, app_name, cred):
4958 """Set the credentials of the Ubuntu SSO item."""
4959 # Creates the secret from the credentials
4960- secret = urllib.urlencode(cred)
4961+ secret = urlencode(cred)
4962
4963 attr = self._get_keyring_attr(app_name)
4964 # Add our SSO credentials to the keyring
4965@@ -95,7 +100,7 @@
4966 item = yield self._find_keyring_item(app_name, attr=attr)
4967 if item is not None:
4968 yield self.set_credentials(app_name,
4969- dict(urlparse.parse_qsl(item.secret)))
4970+ dict(parse_qsl(item.secret)))
4971 yield item.delete()
4972
4973 result = yield self._find_keyring_item(app_name)
4974@@ -114,7 +119,7 @@
4975 if item is not None:
4976 logger.debug("Parsing secret.")
4977 secret = yield item.get_value()
4978- returnValue(dict(urlparse.parse_qsl(secret)))
4979+ returnValue(dict(parse_qsl(secret)))
4980 else:
4981 # if no item found, try getting the old credentials
4982 if app_name == U1_APP_NAME:
4983
4984=== modified file 'ubuntu_sso/main/__init__.py'
4985--- ubuntu_sso/main/__init__.py 2012-06-22 16:51:06 +0000
4986+++ ubuntu_sso/main/__init__.py 2012-07-18 18:54:28 +0000
4987@@ -94,12 +94,10 @@
4988 else:
4989 from ubuntu_sso.main import linux as source
4990
4991- source.get_activation_cmdline = lambda *args: None
4992
4993 UbuntuSSOClient = source.UbuntuSSOClient
4994 UbuntuSSOProxy = source.UbuntuSSOProxy
4995 get_sso_client = source.get_sso_client
4996-get_activation_cmdline = source.get_activation_cmdline
4997
4998
4999 def except_to_errdict(e):
5000
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: