~ubuntu-branches/ubuntu/natty/ubuntu-sso-client/natty

« back to all changes in this revision

Viewing changes to ubuntu_sso/gtk/tests/test_gui.py

  • Committer: Daniel Holbach
  • Date: 2010-11-30 13:57:05 UTC
  • mfrom: (19.1.1 ubuntu-sso-client-1.1.5)
  • Revision ID: daniel.holbach@canonical.com-20101130135705-9iw0623qjcpuvpuq
Tags: 1.1.5-0ubuntu1
* New upstream release (1.1.5):
    * Use "org.freedesktop.secrets" dbus service instead of
    "org.gnome.keyring" (LP: #683088).
* New upstream release (1.1.4):
    * Added a gtk.Notebook to ensure proper window resize at startup
      (LP: #682669).
    * Enabled window resizing to be more user friendly.
    * Remove outdated references to gnome keyring from docstrings.
* New upstream release (1.1.3):
    * Make UI more friendly to resizes and big fonts (LP: #627496).
    * Splitting GUI code out of backend (LP: #677518).
    * Credentials can now be stored using a DBus called (LP: #680253).
    * Status from SSO server is now case sensitive (LP: #653165).
    * Credentials should not be cleared if the ping wasn't made due to empty
      ping url (LP: #676679).
   * Add the utils sub-package to the packages declaration so it installs
   (LP: #680593).
    * Fully async keyring access thru DBus. Drops dependency with
    gnomekeyring (LP: #656545).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
#
 
3
# test_gui - tests for ubuntu_sso.gui
 
4
#
 
5
# Author: Natalia Bidart <natalia.bidart@canonical.com>
 
6
#
 
7
# Copyright 2010 Canonical Ltd.
 
8
#
 
9
# This program is free software: you can redistribute it and/or modify it
 
10
# under the terms of the GNU General Public License version 3, as published
 
11
# by the Free Software Foundation.
 
12
#
 
13
# This program is distributed in the hope that it will be useful, but
 
14
# WITHOUT ANY WARRANTY; without even the implied warranties of
 
15
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
16
# PURPOSE. See the GNU General Public License for more details.
 
17
#
 
18
# You should have received a copy of the GNU General Public License along
 
19
# with this program. If not, see <http://www.gnu.org/licenses/>.
 
20
"""Tests for the GUI for registration/login."""
 
21
 
 
22
import itertools
 
23
import logging
 
24
import os
 
25
 
 
26
from collections import defaultdict
 
27
 
 
28
import dbus
 
29
import gtk
 
30
import webkit
 
31
 
 
32
from twisted.trial.unittest import TestCase
 
33
 
 
34
from contrib.testing.testcase import MementoHandler
 
35
from ubuntu_sso.gtk import gui
 
36
from ubuntu_sso.tests import (APP_NAME, TC_URL, HELP_TEXT, CAPTCHA_ID,
 
37
    CAPTCHA_SOLUTION, EMAIL, EMAIL_TOKEN, NAME, PASSWORD, RESET_PASSWORD_TOKEN)
 
38
 
 
39
 
 
40
# Access to a protected member 'yyy' of a client class
 
41
# pylint: disable=W0212
 
42
 
 
43
# Instance of 'UbuntuSSOClientGUI' has no 'yyy' member
 
44
# pylint: disable=E1101,E1103
 
45
 
 
46
 
 
47
class FakedSSOBackend(object):
 
48
    """Fake a SSO Backend (acts as a dbus.Interface as well)."""
 
49
 
 
50
    def __init__(self, *args, **kwargs):
 
51
        self._args = args
 
52
        self._kwargs = kwargs
 
53
        self._called = {}
 
54
        for i in ('generate_captcha', 'login', 'register_user',
 
55
                  'validate_email', 'request_password_reset_token',
 
56
                  'set_new_password'):
 
57
            setattr(self, i, self._record_call(i))
 
58
 
 
59
    def _record_call(self, func_name):
 
60
        """Store values when calling 'func_name'."""
 
61
 
 
62
        def inner(*args, **kwargs):
 
63
            """Fake 'func_name'."""
 
64
            self._called[func_name] = (args, kwargs)
 
65
 
 
66
        return inner
 
67
 
 
68
 
 
69
class FakedDbusObject(object):
 
70
    """Fake a dbus."""
 
71
 
 
72
    def __init__(self, *args, **kwargs):
 
73
        """Init."""
 
74
        self._args = args
 
75
        self._kwargs = kwargs
 
76
 
 
77
 
 
78
class FakedSessionBus(object):
 
79
    """Fake the session bus."""
 
80
 
 
81
    def __init__(self):
 
82
        """Init."""
 
83
        self.obj = None
 
84
        self.callbacks = {}
 
85
 
 
86
    def add_signal_receiver(self, method, signal_name, dbus_interface):
 
87
        """Add a signal receiver."""
 
88
        self.callbacks[(dbus_interface, signal_name)] = method
 
89
 
 
90
    def remove_signal_receiver(self, match, signal_name, dbus_interface):
 
91
        """Remove the signal receiver."""
 
92
        assert (dbus_interface, signal_name) in self.callbacks
 
93
        del self.callbacks[(dbus_interface, signal_name)]
 
94
 
 
95
    def get_object(self, bus_name, object_path, introspect=True,
 
96
                   follow_name_owner_changes=False, **kwargs):
 
97
        """Return a faked proxy for the given remote object."""
 
98
        if bus_name == gui.DBUS_BUS_NAME and \
 
99
           object_path == gui.DBUS_ACCOUNT_PATH:
 
100
            assert self.obj is None
 
101
            kwargs = dict(object_path=object_path,
 
102
                          bus_name=bus_name, follow_name_owner_changes=True)
 
103
            self.obj = FakedDbusObject(**kwargs)
 
104
        return self.obj
 
105
 
 
106
 
 
107
class Settings(dict):
 
108
    """Faked embedded browser settings."""
 
109
 
 
110
    def get_property(self, prop_name):
 
111
        """Alias for __getitem__."""
 
112
        return self[prop_name]
 
113
 
 
114
    def set_property(self, prop_name, newval):
 
115
        """Alias for __setitem__."""
 
116
        self[prop_name] = newval
 
117
 
 
118
 
 
119
class FakedEmbeddedBrowser(gtk.TextView):
 
120
    """Faked an embedded browser."""
 
121
 
 
122
    def __init__(self):
 
123
        super(FakedEmbeddedBrowser, self).__init__()
 
124
        self._props = {}
 
125
        self._signals = defaultdict(list)
 
126
        self._settings = Settings()
 
127
 
 
128
    def connect(self, signal_name, callback):
 
129
        """Connect 'signal_name' with 'callback'."""
 
130
        self._signals[signal_name].append(callback)
 
131
 
 
132
    def load_uri(self, uri):
 
133
        """Navigate to the given 'uri'."""
 
134
        self._props['uri'] = uri
 
135
 
 
136
    def set_property(self, prop_name, newval):
 
137
        """Set 'prop_name' to 'newval'."""
 
138
        self._props[prop_name] = newval
 
139
 
 
140
    def get_property(self, prop_name):
 
141
        """Return the current value for 'prop_name'."""
 
142
        return self._props[prop_name]
 
143
 
 
144
    def get_settings(self,):
 
145
        """Return the current settings."""
 
146
        return self._settings
 
147
 
 
148
    def get_load_status(self):
 
149
        """Return the current load status."""
 
150
        return gui.WEBKIT_LOAD_FINISHED
 
151
 
 
152
    def show(self):
 
153
        """Show this instance."""
 
154
        self.set_property('visible', True)
 
155
 
 
156
 
 
157
class BasicTestCase(TestCase):
 
158
    """Test case with a helper tracker."""
 
159
 
 
160
    def setUp(self):
 
161
        """Init."""
 
162
        self._called = False  # helper
 
163
 
 
164
        self.memento = MementoHandler()
 
165
        self.memento.setLevel(logging.DEBUG)
 
166
        gui.logger.addHandler(self.memento)
 
167
 
 
168
    def tearDown(self):
 
169
        """Clean up."""
 
170
        self._called = False
 
171
 
 
172
    def _set_called(self, *args, **kwargs):
 
173
        """Set _called to True."""
 
174
        self._called = (args, kwargs)
 
175
 
 
176
 
 
177
class LabeledEntryTestCase(BasicTestCase):
 
178
    """Test suite for the labeled entry."""
 
179
 
 
180
    def setUp(self):
 
181
        """Init."""
 
182
        super(LabeledEntryTestCase, self).setUp()
 
183
        self.label = 'Test me please'
 
184
        self.entry = gui.LabeledEntry(label=self.label)
 
185
 
 
186
        # we need a window to be able to realize ourselves
 
187
        window = gtk.Window()
 
188
        window.add(self.entry)
 
189
        window.show_all()
 
190
 
 
191
    def tearDown(self):
 
192
        """Clean up."""
 
193
        self.entry = None
 
194
        super(LabeledEntryTestCase, self).tearDown()
 
195
 
 
196
    def grab_focus(self, focus_in=True):
 
197
        """Grab focus on widget, if None use self.entry."""
 
198
        direction = 'in' if focus_in else 'out'
 
199
        self.entry.emit('focus-%s-event' % direction, None)
 
200
 
 
201
    # Bad first argument 'LabeledEntry' given to super class
 
202
    # pylint: disable=E1003
 
203
    def assert_correct_label(self):
 
204
        """Check that the entry has the correct label."""
 
205
        # text content is correct
 
206
        msg = 'Text content must be "%s" (got "%s" instead).'
 
207
        expected = self.label
 
208
        actual = super(gui.LabeledEntry, self.entry).get_text()
 
209
        self.assertEqual(expected, actual, msg % (expected, actual))
 
210
 
 
211
        # text color is correct
 
212
        msg = 'Text color must be "%s" (got "%s" instead).'
 
213
        expected = gui.HELP_TEXT_COLOR
 
214
        actual = self.entry.style.text[gtk.STATE_NORMAL]
 
215
        self.assertEqual(expected, actual, msg % (expected, actual))
 
216
 
 
217
    def test_initial_text(self):
 
218
        """Entry have the correct text at startup."""
 
219
        self.assert_correct_label()
 
220
 
 
221
    def test_width_chars(self):
 
222
        """Entry have the correct width."""
 
223
        self.assertEqual(self.entry.get_width_chars(), gui.DEFAULT_WIDTH)
 
224
 
 
225
    def test_tooltip(self):
 
226
        """Entry have the correct tooltip."""
 
227
        msg = 'Tooltip must be "%s" (got "%s" instead).'
 
228
        expected = self.label
 
229
        actual = self.entry.get_tooltip_text()
 
230
        # tooltip is correct
 
231
        self.assertEqual(expected, actual, msg % (expected, actual))
 
232
 
 
233
    def test_clear_entry_on_focus_in(self):
 
234
        """Entry are cleared when focused."""
 
235
        self.grab_focus()
 
236
 
 
237
        msg = 'Entry must be cleared on focus in.'
 
238
        self.assertEqual('', self.entry.get_text(), msg)
 
239
 
 
240
    def test_text_defaults_to_theme_color_when_focus_in(self):
 
241
        """Entry restore its text color when focused in."""
 
242
        self.patch(self.entry, 'modify_text', self._set_called)
 
243
 
 
244
        self.grab_focus()
 
245
 
 
246
        self.assertEqual(((gtk.STATE_NORMAL, None), {}), self._called,
 
247
                         'Entry text color must be restore on focus in.')
 
248
 
 
249
    def test_refill_entry_on_focus_out_if_no_input(self):
 
250
        """Entry is re-filled with label when focused out if no user input."""
 
251
 
 
252
        self.grab_focus()  # grab focus
 
253
        self.grab_focus(focus_in=False)  # loose focus
 
254
 
 
255
        # Entry must be re-filled on focus out
 
256
        self.assert_correct_label()
 
257
 
 
258
    def test_refill_entry_on_focus_out_if_empty_input(self):
 
259
        """Entry is re-filled with label when focused out if empty input."""
 
260
 
 
261
        self.grab_focus()  # grab focus
 
262
 
 
263
        self.entry.set_text('        ')  # add empty text to the entry
 
264
 
 
265
        self.grab_focus(focus_in=False)  # loose focus
 
266
 
 
267
        # Entry must be re-filled on focus out
 
268
        self.assert_correct_label()
 
269
 
 
270
    def test_preserve_input_on_focus_out_if_user_input(self):
 
271
        """Entry is unmodified when focused out if user input."""
 
272
        msg = 'Entry must be left unmodified on focus out when user input.'
 
273
        expected = 'test me please'
 
274
 
 
275
        self.grab_focus()  # grab focus
 
276
 
 
277
        self.entry.set_text(expected)  # add empty text to the entry
 
278
 
 
279
        self.grab_focus(focus_in=False)  # loose focus
 
280
 
 
281
        self.assertEqual(expected, self.entry.get_text(), msg)
 
282
 
 
283
    def test_preserve_input_on_focus_out_and_in_again(self):
 
284
        """Entry is unmodified when focused out and then in again."""
 
285
        msg = 'Entry must be left unmodified on focus out and then in again.'
 
286
        expected = 'test me I mean it'
 
287
 
 
288
        self.grab_focus()  # grab focus
 
289
 
 
290
        self.entry.set_text(expected)  # add text to the entry
 
291
 
 
292
        self.grab_focus(focus_in=False)  # loose focus
 
293
        self.grab_focus()  # grab focus again!
 
294
 
 
295
        self.assertEqual(expected, self.entry.get_text(), msg)
 
296
 
 
297
    def test_get_text_ignores_label(self):
 
298
        """Entry's text is only user input (label is ignored)."""
 
299
        self.assertEqual(self.entry.get_text(), '')
 
300
 
 
301
    def test_get_text_ignores_empty_input(self):
 
302
        """Entry's text is only user input (empty text is ignored)."""
 
303
        self.entry.set_text('       ')
 
304
        self.assertEqual(self.entry.get_text(), '')
 
305
 
 
306
    def test_get_text_doesnt_ignore_user_input(self):
 
307
        """Entry's text is user input."""
 
308
        self.entry.set_text('a')
 
309
        self.assertEqual(self.entry.get_text(), 'a')
 
310
 
 
311
    def test_no_warning_by_default(self):
 
312
        """No secondary icon by default."""
 
313
        self.assertEqual(self.entry.warning, None)
 
314
        self.assertEqual(self.entry.get_property('secondary-icon-stock'),
 
315
                         None)
 
316
        self.assertEqual(self.entry.get_property('secondary-icon-sensitive'),
 
317
                         False)
 
318
        self.assertEqual(self.entry.get_property('secondary-icon-activatable'),
 
319
                         False)
 
320
        prop = self.entry.get_property('secondary-icon-tooltip-text')
 
321
        self.assertEqual(prop, None)
 
322
 
 
323
    def test_set_warning(self):
 
324
        """Setting a warning show the proper secondary icon."""
 
325
        msg = 'You failed!'
 
326
        self.entry.set_warning(msg)
 
327
        self.assertEqual(self.entry.warning, msg)
 
328
        self.assertEqual(self.entry.get_property('secondary-icon-stock'),
 
329
                         gtk.STOCK_DIALOG_WARNING)
 
330
        self.assertEqual(self.entry.get_property('secondary-icon-sensitive'),
 
331
                         True)
 
332
        self.assertEqual(self.entry.get_property('secondary-icon-activatable'),
 
333
                         False)
 
334
        prop = self.entry.get_property('secondary-icon-tooltip-text')
 
335
        self.assertEqual(prop, msg)
 
336
 
 
337
    def test_clear_warning(self):
 
338
        """Clearing a warning no longer show the secondary icon."""
 
339
        self.entry.clear_warning()
 
340
        self.assertEqual(self.entry.warning, None)
 
341
        self.assertEqual(self.entry.get_property('secondary-icon-stock'),
 
342
                         None)
 
343
        self.assertEqual(self.entry.get_property('secondary-icon-sensitive'),
 
344
                         False)
 
345
        self.assertEqual(self.entry.get_property('secondary-icon-activatable'),
 
346
                         False)
 
347
        prop = self.entry.get_property('secondary-icon-tooltip-text')
 
348
        self.assertEqual(prop, None)
 
349
 
 
350
 
 
351
class PasswordLabeledEntryTestCase(LabeledEntryTestCase):
 
352
    """Test suite for the labeled entry when is_password is True."""
 
353
 
 
354
    def setUp(self):
 
355
        """Init."""
 
356
        super(PasswordLabeledEntryTestCase, self).setUp()
 
357
        self.entry.is_password = True
 
358
 
 
359
    def test_password_fields_are_visible_at_startup(self):
 
360
        """Password entrys show the helping text at startup."""
 
361
        self.assertTrue(self.entry.get_visibility(),
 
362
                        'Password entry should be visible at start up.')
 
363
 
 
364
    def test_password_field_is_visible_if_no_input_and_focus_out(self):
 
365
        """Password entry show the label when focus out."""
 
366
        self.grab_focus()  # user cliked or TAB'd to the entry
 
367
        self.grab_focus(focus_in=False)  # loose focus
 
368
        self.assertTrue(self.entry.get_visibility(),
 
369
                        'Entry should be visible when focus out and no input.')
 
370
 
 
371
    def test_password_fields_are_not_visible_when_editing(self):
 
372
        """Password entrys show the hidden chars instead of the password."""
 
373
        self.grab_focus()  # user cliked or TAB'd to the entry
 
374
        self.assertFalse(self.entry.get_visibility(),
 
375
                         'Entry should not be visible when editing.')
 
376
 
 
377
 
 
378
class UbuntuSSOClientTestCase(BasicTestCase):
 
379
    """Basic setup and helper functions."""
 
380
 
 
381
    gui_class = gui.UbuntuSSOClientGUI
 
382
    kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT)
 
383
 
 
384
    def setUp(self):
 
385
        """Init."""
 
386
        super(UbuntuSSOClientTestCase, self).setUp()
 
387
        self.patch(dbus, 'SessionBus', FakedSessionBus)
 
388
        self.patch(dbus, 'Interface', FakedSSOBackend)
 
389
        self.pages = ('enter_details', 'processing', 'verify_email', 'finish',
 
390
                      'tc_browser', 'login', 'request_password_token',
 
391
                      'set_new_password')
 
392
        self.ui = self.gui_class(**self.kwargs)
 
393
        self.error = {'message': self.ui.UNKNOWN_ERROR}
 
394
 
 
395
    def tearDown(self):
 
396
        """Clean up."""
 
397
        self.ui.bus.callbacks = {}
 
398
        self.ui = None
 
399
        super(UbuntuSSOClientTestCase, self).tearDown()
 
400
 
 
401
    def assert_entries_are_packed_to_ui(self, container_name, entries):
 
402
        """Every entry is properly packed in the ui 'container_name'."""
 
403
        msg = 'Entry "%s" must be packed in "%s" but is not.'
 
404
        container = getattr(self.ui, container_name)
 
405
        for kind in entries:
 
406
            name = '%s_entry' % kind
 
407
            entry = getattr(self.ui, name)
 
408
            self.assertIsInstance(entry, gui.LabeledEntry)
 
409
            self.assertIn(entry, container, msg % (name, container_name))
 
410
 
 
411
    def assert_warnings_visibility(self, visible=False):
 
412
        """Every warning label should be 'visible'."""
 
413
        msg = '"%s" should have %sempty content.'
 
414
        for name in self.ui.widgets:
 
415
            widget = getattr(self.ui, name)
 
416
            if 'warning' in name:
 
417
                self.assertEqual('', widget.get_text(),
 
418
                                 msg % (name, '' if visible else 'non-'))
 
419
            elif 'entry' in name:
 
420
                self.assertEqual(widget.warning, '')
 
421
 
 
422
    def assert_correct_label_warning(self, label, message):
 
423
        """Check that a warning is shown displaying 'message'."""
 
424
        # warning label is visible
 
425
        self.assertTrue(label.get_property('visible'))
 
426
 
 
427
        # warning content is correct
 
428
        actual = label.get_text()
 
429
        self.assertEqual(actual, message)
 
430
 
 
431
        # content color is correct
 
432
        expected = gui.WARNING_TEXT_COLOR
 
433
        actual = label.style.fg[gtk.STATE_NORMAL]
 
434
        self.assertEqual(expected, actual)  # until realized this will fail
 
435
 
 
436
    def assert_correct_entry_warning(self, entry, message):
 
437
        """Check that a warning is shown displaying 'message'."""
 
438
        self.assertEqual(entry.warning, message)
 
439
 
 
440
    def assert_pages_visibility(self, **kwargs):
 
441
        """The page 'name' is the current page for the content notebook."""
 
442
        msg = 'page %r must be self.ui.content\'s current page.'
 
443
        (name, _), = kwargs.items()
 
444
        page = getattr(self.ui, '%s_vbox' % name)
 
445
        self.assertEqual(self.ui.content.get_current_page(),
 
446
                         self.ui.content.page_num(page), msg % name)
 
447
 
 
448
    def click_join_with_valid_data(self):
 
449
        """Move to the next page after entering details."""
 
450
        self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=CAPTCHA_ID)
 
451
 
 
452
        self.ui.name_entry.set_text(NAME)
 
453
        # match emails
 
454
        self.ui.email1_entry.set_text(EMAIL)
 
455
        self.ui.email2_entry.set_text(EMAIL)
 
456
        # match passwords
 
457
        self.ui.password1_entry.set_text(PASSWORD)
 
458
        self.ui.password2_entry.set_text(PASSWORD)
 
459
        # agree to TC
 
460
        self.ui.yes_to_tc_checkbutton.set_active(True)
 
461
        # resolve captcha properly
 
462
        self.ui.captcha_solution_entry.set_text(CAPTCHA_SOLUTION)
 
463
 
 
464
        self.ui.join_ok_button.clicked()
 
465
 
 
466
    def click_verify_email_with_valid_data(self):
 
467
        """Move to the next page after entering email token."""
 
468
        self.click_join_with_valid_data()
 
469
 
 
470
        # resolve email token properly
 
471
        self.ui.email_token_entry.set_text(EMAIL_TOKEN)
 
472
 
 
473
        self.ui.verify_token_button.clicked()
 
474
 
 
475
    def click_connect_with_valid_data(self):
 
476
        """Move to the next page after entering login info."""
 
477
        # enter email
 
478
        self.ui.login_email_entry.set_text(EMAIL)
 
479
        # enter password
 
480
        self.ui.login_password_entry.set_text(PASSWORD)
 
481
 
 
482
        self.ui.login_ok_button.clicked()
 
483
 
 
484
    def click_request_password_token_with_valid_data(self):
 
485
        """Move to the next page after requesting for password reset token."""
 
486
        # enter email
 
487
        self.ui.reset_email_entry.set_text(EMAIL)
 
488
 
 
489
        self.ui.request_password_token_ok_button.clicked()
 
490
 
 
491
    def click_set_new_password_with_valid_data(self):
 
492
        """Move to the next page after resetting password."""
 
493
        # enter reset code
 
494
        self.ui.reset_code_entry.set_text(RESET_PASSWORD_TOKEN)
 
495
        # match passwords
 
496
        self.ui.reset_password1_entry.set_text(PASSWORD)
 
497
        self.ui.reset_password2_entry.set_text(PASSWORD)
 
498
 
 
499
        self.ui.set_new_password_ok_button.clicked()
 
500
 
 
501
 
 
502
class BasicUbuntuSSOClientTestCase(UbuntuSSOClientTestCase):
 
503
    """Test suite for basic functionality."""
 
504
 
 
505
    def test_main_window_is_visible_at_startup(self):
 
506
        """The main window is shown at startup."""
 
507
        self.assertTrue(self.ui.window.get_property('visible'))
 
508
 
 
509
    def test_main_window_is_resizable(self):
 
510
        """The main window can be resized."""
 
511
        self.assertTrue(self.ui.window.get_property('resizable'))
 
512
 
 
513
    def test_closing_main_window_calls_close_callback(self):
 
514
        """The close_callback is called when closing the main window."""
 
515
        self.ui.close_callback = self._set_called
 
516
        self.ui.on_close_clicked()
 
517
        self.assertTrue(self._called,
 
518
                        'close_callback was called when window was closed.')
 
519
 
 
520
    def test_app_name_is_stored(self):
 
521
        """The app_name is stored for further use."""
 
522
        self.assertIn(APP_NAME, self.ui.app_name)
 
523
 
 
524
    def test_session_bus_is_correct(self):
 
525
        """The session bus is created and is correct."""
 
526
        self.assertIsInstance(self.ui.bus, FakedSessionBus)
 
527
 
 
528
    def test_iface_name_is_correct(self):
 
529
        """The session bus is created and is correct."""
 
530
        self.assertEqual(self.ui.iface_name, gui.DBUS_IFACE_USER_NAME)
 
531
 
 
532
    def test_bus_object_is_created(self):
 
533
        """SessionBus.get_object is called properly."""
 
534
        self.assertIsInstance(self.ui.bus.obj, FakedDbusObject)
 
535
        self.assertEqual(self.ui.bus.obj._args, ())
 
536
        expected = dict(object_path=gui.DBUS_ACCOUNT_PATH,
 
537
                        bus_name=gui.DBUS_BUS_NAME,
 
538
                        follow_name_owner_changes=True)
 
539
        self.assertEqual(expected, self.ui.bus.obj._kwargs)
 
540
 
 
541
    def test_bus_interface_is_created(self):
 
542
        """dbus.Interface is called properly."""
 
543
        self.assertIsInstance(self.ui.backend, FakedSSOBackend)
 
544
        self.assertEqual(self.ui.backend._args, ())
 
545
        expected = dict(object=self.ui.bus.obj,
 
546
                        dbus_interface=gui.DBUS_IFACE_USER_NAME)
 
547
        self.assertEqual(expected, self.ui.backend._kwargs)
 
548
 
 
549
    def test_dbus_signals_are_removed(self):
 
550
        """The hooked signals are removed at shutdown time."""
 
551
        self.ui._setup_signals()
 
552
        assert len(self.ui.bus.callbacks) > 0  # at least one callback
 
553
 
 
554
        self.ui.on_close_clicked()
 
555
 
 
556
        self.assertEqual(self.ui.bus.callbacks, {})
 
557
 
 
558
    def test_close_callback(self):
 
559
        """A close_callback parameter is called when closing the window."""
 
560
        ui = self.gui_class(close_callback=self._set_called, **self.kwargs)
 
561
        ui.on_close_clicked()
 
562
        self.assertTrue(self._called, 'close_callback was called on close.')
 
563
 
 
564
    def test_close_callback_if_none(self):
 
565
        """A close_callback parameter is not called if is None."""
 
566
        ui = self.gui_class(close_callback=None, **self.kwargs)
 
567
        ui.on_close_clicked()
 
568
        # no crash when close_callback is None
 
569
 
 
570
    def test_pages_are_packed_into_container(self):
 
571
        """All the pages are packed in the main container."""
 
572
        children = self.ui.content.get_children()
 
573
        for page_name in self.pages:
 
574
            page = getattr(self.ui, '%s_vbox' % page_name)
 
575
            self.assertIn(page, children)
 
576
 
 
577
    def test_initial_text_for_entries(self):
 
578
        """Entries have the correct text at startup."""
 
579
        msg = 'Text for "%s" must be "%s" (got "%s" instead).'
 
580
        for name in self.ui.entries:
 
581
            entry = getattr(self.ui, name)
 
582
            expected = getattr(self.ui, name.upper())
 
583
            actual = entry.label
 
584
            # text content is correct
 
585
            self.assertEqual(expected, actual, msg % (name, expected, actual))
 
586
 
 
587
    def test_entries_activates_default(self):
 
588
        """Entries have the activates default prop set."""
 
589
        msg = '"%s" must have activates_default set to True.'
 
590
        for name in self.ui.entries:
 
591
            entry = getattr(self.ui, name)
 
592
            self.assertTrue(entry.get_activates_default(), msg % (name,))
 
593
 
 
594
    def test_label_size_allocated_is_connected(self):
 
595
        """Labels have the size-allocate signal connected."""
 
596
        msg = 'Label %r must have size-allocate connected.'
 
597
        labels = [i for i in self.ui.widgets if 'label' in i]
 
598
        for label in labels:
 
599
            widget = getattr(self.ui, label)
 
600
            widget.emit('size-allocate', gtk.gdk.Rectangle(1, 2, 3, 4))
 
601
            self.assertEqual(widget.get_size_request(), (3 - 2, -1),
 
602
                             msg % (label,))
 
603
 
 
604
    def test_password_fields_are_password(self):
 
605
        """Password fields have the is_password flag set."""
 
606
        msg = '"%s" should be a password LabeledEntry instance.'
 
607
        passwords = filter(lambda name: 'password' in name,
 
608
                           self.ui.entries)
 
609
        for name in passwords:
 
610
            widget = getattr(self.ui, name)
 
611
            self.assertTrue(widget.is_password, msg % name)
 
612
 
 
613
    def test_warning_fields_are_cleared(self):
 
614
        """Every warning label should be cleared."""
 
615
        self.assert_warnings_visibility()
 
616
 
 
617
    def test_cancel_buttons_close_window(self):
 
618
        """Every cancel button should close the window when clicked."""
 
619
        msg = '"%s" should close the window when clicked.'
 
620
        buttons = filter(lambda name: 'cancel_button' in name or
 
621
                                      'close_button' in name, self.ui.widgets)
 
622
        for name in buttons:
 
623
            self.ui = self.gui_class(close_callback=self._set_called,
 
624
                                     **self.kwargs)
 
625
            widget = getattr(self.ui, name)
 
626
            widget.clicked()
 
627
            self.assertEqual(self._called, ((widget,), {}), msg % name)
 
628
            self._called = False
 
629
 
 
630
    def test_window_icon(self):
 
631
        """Main window has the proper icon."""
 
632
        self.assertEqual('ubuntu-logo', self.ui.window.get_icon_name())
 
633
 
 
634
    def test_transient_window_is_none_if_window_id_is_zero(self):
 
635
        """The transient window is correct."""
 
636
        self.patch(gtk.gdk, 'window_foreign_new', self._set_called)
 
637
        self.gui_class(window_id=0, **self.kwargs)
 
638
        self.assertFalse(self._called, 'set_transient_for must not be called.')
 
639
 
 
640
    def test_transient_window_is_correct(self):
 
641
        """The transient window is correct."""
 
642
        xid = 5
 
643
        self.patch(gtk.gdk, 'window_foreign_new', self._set_called)
 
644
        self.gui_class(window_id=xid, **self.kwargs)
 
645
        self.assertTrue(self.memento.check(logging.ERROR, 'set_transient_for'))
 
646
        self.assertTrue(self.memento.check(logging.ERROR, str(xid)))
 
647
        self.assertEqual(self._called, ((xid,), {}))
 
648
 
 
649
    def test_transient_window_accepts_negative_id(self):
 
650
        """The transient window accepts a negative window id."""
 
651
        xid = -5
 
652
        self.patch(gtk.gdk, 'window_foreign_new', self._set_called)
 
653
        self.gui_class(window_id=xid, **self.kwargs)
 
654
        self.assertEqual(self._called, ((xid,), {}))
 
655
 
 
656
    def test_finish_success_shows_success_page(self):
 
657
        """When calling 'finish_success' the success page is shown."""
 
658
        self.ui.finish_success()
 
659
        self.assert_pages_visibility(finish=True)
 
660
        self.assertEqual(self.ui.SUCCESS, self.ui.finish_vbox.label.get_text())
 
661
 
 
662
    def test_finish_error_shows_error_page(self):
 
663
        """When calling 'finish_error' the error page is shown."""
 
664
        self.ui.finish_error(error=self.error)
 
665
        self.assert_pages_visibility(finish=True)
 
666
        self.assertEqual(self.ui.ERROR, self.ui.finish_vbox.label.get_text())
 
667
 
 
668
 
 
669
class EnterDetailsTestCase(UbuntuSSOClientTestCase):
 
670
    """Test suite for the user registration (enter details page)."""
 
671
 
 
672
    def test_initial_text_for_header_label(self):
 
673
        """The header must have the correct text at startup."""
 
674
        msg = 'Text for the header must be "%s" (got "%s" instead).'
 
675
        expected = self.ui.JOIN_HEADER_LABEL % {'app_name': APP_NAME}
 
676
        actual = self.ui.header_label.get_text()
 
677
        # text content is correct
 
678
        self.assertEqual(expected, actual, msg % (expected, actual))
 
679
 
 
680
    def test_entries_are_packed_to_ui(self):
 
681
        """Every entry is properly packed in the ui."""
 
682
        for kind in ('email', 'password'):
 
683
            container_name = '%ss_hbox' % kind
 
684
            entries = ('%s%s' % (kind, i) for i in xrange(1, 3))
 
685
            self.assert_entries_are_packed_to_ui(container_name, entries)
 
686
 
 
687
        self.assert_entries_are_packed_to_ui('enter_details_vbox', ('name',))
 
688
        self.assert_entries_are_packed_to_ui('captcha_solution_vbox',
 
689
                                             ('captcha_solution',))
 
690
        self.assert_entries_are_packed_to_ui('verify_email_details_vbox',
 
691
                                             ('email_token',))
 
692
 
 
693
    def test_initial_texts_for_checkbuttons(self):
 
694
        """Check buttons have the correct text at startup."""
 
695
        msg = 'Text for "%s" must be "%s" (got "%s" instead).'
 
696
        expected = self.ui.YES_TO_UPDATES % {'app_name': APP_NAME}
 
697
        actual = self.ui.yes_to_updates_checkbutton.get_label()
 
698
        self.assertEqual(expected, actual, msg % ('yes_to_updates_checkbutton',
 
699
                                                  expected, actual))
 
700
        expected = self.ui.YES_TO_TC % {'app_name': APP_NAME}
 
701
        actual = self.ui.yes_to_tc_checkbutton.get_label()
 
702
        self.assertEqual(expected, actual,
 
703
                         msg % ('yes_to_tc_checkbutton', expected, actual))
 
704
 
 
705
    def test_checkbuttons_are_checked_at_startup(self):
 
706
        """Checkbuttons are checked by default."""
 
707
        msg = '"%s" is checked by default.'
 
708
        for name in ('yes_to_updates_checkbutton', 'yes_to_tc_checkbutton'):
 
709
            widget = getattr(self.ui, name)
 
710
            self.assertTrue(widget.get_active(), msg % name)
 
711
 
 
712
    def test_vboxes_visible_properties(self):
 
713
        """Only 'enter_details' vbox is visible at start up."""
 
714
        self.assert_pages_visibility(enter_details=True)
 
715
 
 
716
    def test_join_ok_button_clicked(self):
 
717
        """Clicking 'join_ok_button' sends info to backend using 'register'."""
 
718
        self.click_join_with_valid_data()
 
719
 
 
720
        # assert register_user was called
 
721
        expected = 'register_user'
 
722
        self.assertIn(expected, self.ui.backend._called)
 
723
        self.assertEqual(self.ui.backend._called[expected],
 
724
                         ((APP_NAME, EMAIL, PASSWORD, CAPTCHA_ID,
 
725
                           CAPTCHA_SOLUTION),
 
726
                          dict(reply_handler=gui.NO_OP,
 
727
                               error_handler=gui.NO_OP)))
 
728
 
 
729
    def test_join_ok_button_clicked_morphs_to_processing_page(self):
 
730
        """Clicking 'join_ok_button' presents the processing vbox."""
 
731
        self.click_join_with_valid_data()
 
732
        self.assert_pages_visibility(processing=True)
 
733
 
 
734
    def test_processing_vbox_displays_an_active_spinner(self):
 
735
        """When processing the registration, an active spinner is shown."""
 
736
        self.click_join_with_valid_data()
 
737
 
 
738
        self.assertTrue(self.ui.processing_vbox.get_property('visible'),
 
739
                        'the processing box should be visible.')
 
740
 
 
741
        box = self.ui.processing_vbox.get_children()[0].get_children()[0]
 
742
        self.assertEqual(2, len(box.get_children()),
 
743
                         'processing_vbox must have two children.')
 
744
 
 
745
        spinner, label = box.get_children()
 
746
        self.assertIsInstance(spinner, gtk.Spinner)
 
747
        self.assertIsInstance(label, gtk.Label)
 
748
 
 
749
        self.assertTrue(spinner.get_property('visible'),
 
750
                        'the processing spinner should be visible.')
 
751
        self.assertTrue(spinner.get_property('active'),
 
752
                        'the processing spinner should be active.')
 
753
        self.assertTrue(label.get_property('visible'),
 
754
                        'the processing label should be visible.')
 
755
        self.assertEqual(label.get_text(), self.ui.ONE_MOMENT_PLEASE,
 
756
                        'the processing label text must be correct.')
 
757
 
 
758
    def test_captcha_image_is_not_visible_at_startup(self):
 
759
        """Captcha image is not shown at startup."""
 
760
        self.assertFalse(self.ui.captcha_image.get_property('visible'),
 
761
                        'the captcha_image should not be visible.')
 
762
 
 
763
    def test_captcha_filename_is_different_each_time(self):
 
764
        """The captcha image is different each time."""
 
765
        ui = self.gui_class(**self.kwargs)
 
766
        self.assertNotEqual(self.ui._captcha_filename, ui._captcha_filename)
 
767
 
 
768
    def test_captcha_image_is_removed_when_exiting(self):
 
769
        """The captcha image is removed at shutdown time."""
 
770
        open(self.ui._captcha_filename, 'w').close()
 
771
        assert os.path.exists(self.ui._captcha_filename)
 
772
        self.ui.on_close_clicked()
 
773
 
 
774
        self.assertFalse(os.path.exists(self.ui._captcha_filename),
 
775
                         'captcha image must be removed when exiting.')
 
776
 
 
777
    def test_captcha_image_is_a_spinner_at_first(self):
 
778
        """Captcha image shows a spinner until the image is downloaded."""
 
779
        self.assertTrue(self.ui.captcha_loading.get_property('visible'),
 
780
                        'the captcha_loading box should be visible.')
 
781
 
 
782
        box = self.ui.captcha_loading.get_children()[0].get_children()[0]
 
783
        self.assertEqual(2, len(box.get_children()),
 
784
                         'captcha_loading must have two children.')
 
785
 
 
786
        spinner, label = box.get_children()
 
787
        self.assertIsInstance(spinner, gtk.Spinner)
 
788
        self.assertIsInstance(label, gtk.Label)
 
789
 
 
790
        self.assertTrue(spinner.get_property('visible'),
 
791
                        'the captcha_loading spinner should be visible.')
 
792
        self.assertTrue(spinner.get_property('active'),
 
793
                        'the captcha_loading spinner should be active.')
 
794
        self.assertTrue(label.get_property('visible'),
 
795
                        'the captcha_loading label should be visible.')
 
796
        self.assertEqual(label.get_text(), self.ui.LOADING,
 
797
                        'the captcha_loading label text must be correct.')
 
798
 
 
799
    def test_join_ok_button_is_disabled_until_captcha_is_available(self):
 
800
        """The join_ok_button is not sensitive until captcha is available."""
 
801
        self.assertFalse(self.ui.join_ok_button.is_sensitive())
 
802
 
 
803
    def test_join_ok_button_is_enabled_when_captcha_is_available(self):
 
804
        """The join_ok_button is sensitive when captcha is available."""
 
805
        self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=CAPTCHA_ID)
 
806
        self.assertTrue(self.ui.join_ok_button.is_sensitive())
 
807
 
 
808
    def test_captcha_loading_is_hid_when_captcha_is_available(self):
 
809
        """The captcha_loading is hid when captcha is available."""
 
810
        self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=CAPTCHA_ID)
 
811
        self.assertFalse(self.ui.captcha_loading.get_property('visible'),
 
812
                         'captcha_loading is not visible.')
 
813
 
 
814
    def test_captcha_id_is_stored_when_captcha_is_available(self):
 
815
        """The captcha_id is stored when captcha is available."""
 
816
        self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=CAPTCHA_ID)
 
817
        self.assertEqual(CAPTCHA_ID, self.ui._captcha_id)
 
818
 
 
819
    def test_captcha_image_is_requested_as_startup(self):
 
820
        """The captcha image is requested at startup."""
 
821
        # assert generate_captcha was called
 
822
        expected = 'generate_captcha'
 
823
        self.assertIn(expected, self.ui.backend._called)
 
824
        self.assertEqual(self.ui.backend._called[expected],
 
825
                         ((APP_NAME, self.ui._captcha_filename),
 
826
                          dict(reply_handler=gui.NO_OP,
 
827
                               error_handler=gui.NO_OP)))
 
828
 
 
829
    def test_captcha_is_shown_when_available(self):
 
830
        """The captcha image is shown when available."""
 
831
        self.patch(self.ui.captcha_image, 'set_from_file', self._set_called)
 
832
        self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=CAPTCHA_ID)
 
833
        self.assertTrue(self.ui.captcha_image.get_property('visible'))
 
834
        self.assertEqual(self._called, ((self.ui._captcha_filename,), {}))
 
835
 
 
836
    def test_on_captcha_generated_logs_captcha_id_when_none(self):
 
837
        """If the captcha id is None, a warning is logged."""
 
838
        self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=None)
 
839
        self.assertTrue(self.memento.check(logging.WARNING, APP_NAME))
 
840
        self.assertTrue(self.memento.check(logging.WARNING,
 
841
                                           'captcha_id is None'))
 
842
 
 
843
    def test_captcha_reload_button_visible(self):
 
844
        """The captcha reload button is initially visible."""
 
845
        self.assertTrue(self.ui.captcha_reload_button.get_visible(),
 
846
                        "The captcha button is not visible")
 
847
 
 
848
    def test_captcha_reload_button_reloads_captcha(self):
 
849
        """The captcha reload button loads a new captcha."""
 
850
        self.ui.on_captcha_generated(app_name=APP_NAME, captcha_id=CAPTCHA_ID)
 
851
        self.patch(self.ui, '_generate_captcha', self._set_called)
 
852
        self.ui.captcha_reload_button.clicked()
 
853
        self.assertEqual(self._called, ((), {}))
 
854
 
 
855
    def test_captcha_reload_button_has_tooltip(self):
 
856
        """The captcha reload button has a tooltip."""
 
857
        self.assertEqual(self.ui.captcha_reload_button.get_tooltip_text(),
 
858
                         self.ui.CAPTCHA_RELOAD_TOOLTIP)
 
859
 
 
860
    def test_login_button_has_correct_wording(self):
 
861
        """The sign in button has the proper wording."""
 
862
        actual = self.ui.login_button.get_label()
 
863
        self.assertEqual(self.ui.LOGIN_BUTTON_LABEL, actual)
 
864
 
 
865
    def test_join_ok_button_does_nothing_if_clicked_but_disabled(self):
 
866
        """The join form can only be submitted if the button is sensitive."""
 
867
        self.patch(self.ui.email1_entry, 'get_text', self._set_called)
 
868
 
 
869
        self.ui.join_ok_button.set_sensitive(False)
 
870
        self.ui.join_ok_button.clicked()
 
871
        self.assertFalse(self._called)
 
872
 
 
873
        self.ui.join_ok_button.set_sensitive(True)
 
874
        self.ui.join_ok_button.clicked()
 
875
        self.assertTrue(self._called)
 
876
 
 
877
    def test_user_and_pass_are_cached(self):
 
878
        """Username and password are temporarly cached for further use."""
 
879
        self.click_join_with_valid_data()
 
880
        self.assertEqual(self.ui.user_email, EMAIL)
 
881
        self.assertEqual(self.ui.user_password, PASSWORD)
 
882
 
 
883
    def test_on_captcha_generation_error(self):
 
884
        """on_captcha_generation_error shows an error and reloads captcha."""
 
885
        self.patch(self.ui, '_generate_captcha', self._set_called)
 
886
        self.ui.on_captcha_generation_error(APP_NAME, error=self.error)
 
887
        self.assert_correct_label_warning(self.ui.warning_label,
 
888
                                          self.ui.CAPTCHA_LOAD_ERROR)
 
889
        self.assertEqual(self._called, ((), {}))
 
890
 
 
891
 
 
892
class NoTermsAndConditionsTestCase(UbuntuSSOClientTestCase):
 
893
    """Test suite for the user registration (with no t&c link)."""
 
894
 
 
895
    kwargs = dict(app_name=APP_NAME, tc_url='', help_text=HELP_TEXT)
 
896
 
 
897
    def test_no_tc_link(self):
 
898
        """The T&C button and checkbox are not shown if no link is provided"""
 
899
        self.assertEqual(self.ui.tc_vbox.get_visible(), False)
 
900
 
 
901
 
 
902
class TermsAndConditionsTestCase(UbuntuSSOClientTestCase):
 
903
    """Test suite for the user registration (terms & conditions page)."""
 
904
 
 
905
    def test_has_tc_link(self):
 
906
        """The T&C button and checkbox are shown if the link is provided"""
 
907
        self.assertEqual(self.ui.tc_button.get_visible(), True)
 
908
        self.assertEqual(self.ui.yes_to_tc_checkbutton.get_visible(), True)
 
909
 
 
910
 
 
911
class TermsAndConditionsBrowserTestCase(UbuntuSSOClientTestCase):
 
912
    """Test suite for the terms & conditions browser."""
 
913
 
 
914
    def setUp(self):
 
915
        super(TermsAndConditionsBrowserTestCase, self).setUp()
 
916
        self.patch(webkit, 'WebView', FakedEmbeddedBrowser)
 
917
 
 
918
        self.ui.tc_button.clicked()
 
919
        children = self.ui.tc_browser_window.get_children()
 
920
        assert len(children) == 1
 
921
        self.browser = children[0]
 
922
 
 
923
    def tearDown(self):
 
924
        self.ui.tc_browser_vbox.hide()
 
925
        super(TermsAndConditionsBrowserTestCase, self).tearDown()
 
926
 
 
927
    def test_tc_browser_is_created_when_tc_page_is_shown(self):
 
928
        """The browser is created when the TC button is clicked."""
 
929
        self.ui.on_tc_browser_notify_load_status(self.browser)
 
930
 
 
931
        children = self.ui.tc_browser_window.get_children()
 
932
        self.assertEqual(1, len(children))
 
933
 
 
934
    def test_is_visible(self):
 
935
        """The browser is visible."""
 
936
        self.assertIsInstance(self.browser, FakedEmbeddedBrowser)
 
937
        self.assertTrue(self.browser.get_property('visible'))
 
938
 
 
939
    def test_settings(self):
 
940
        """The browser settings are correct."""
 
941
        settings = self.browser.get_settings()
 
942
        self.assertFalse(settings.get_property('enable-plugins'))
 
943
        self.assertFalse(settings.get_property('enable-default-context-menu'))
 
944
 
 
945
    def test_tc_browser_is_destroyed_when_tc_page_is_hid(self):
 
946
        """The browser is destroyed when the TC page is hid."""
 
947
        self.ui.on_tc_browser_notify_load_status(self.browser)
 
948
        self.patch(self.browser, 'destroy', self._set_called)
 
949
        self.ui.tc_browser_vbox.hide()
 
950
        self.assertEqual(self._called, ((), {}))
 
951
 
 
952
    def test_tc_browser_is_removed_when_tc_page_is_hid(self):
 
953
        """The browser is removed when the TC page is hid."""
 
954
        self.ui.on_tc_browser_notify_load_status(self.browser)
 
955
 
 
956
        self.ui.tc_browser_vbox.hide()
 
957
 
 
958
        children = self.ui.tc_browser_window.get_children()
 
959
        self.assertEqual(0, len(children))
 
960
 
 
961
    def test_tc_button_clicked_morphs_into_processing_page(self):
 
962
        """Clicking the T&C button morphs into processing page."""
 
963
        self.assert_pages_visibility(processing=True)
 
964
 
 
965
    def test_tc_back_clicked_returns_to_previous_page(self):
 
966
        """Terms & Conditions back button return to previous page."""
 
967
        self.ui.on_tc_browser_notify_load_status(self.browser)
 
968
        self.ui.tc_back_button.clicked()
 
969
        self.assert_pages_visibility(enter_details=True)
 
970
 
 
971
    def test_tc_button_has_the_proper_wording(self):
 
972
        """Terms & Conditions has the proper wording."""
 
973
        self.assertEqual(self.ui.tc_button.get_label(), self.ui.TC_BUTTON)
 
974
 
 
975
    def test_tc_has_no_help_text(self):
 
976
        """The help text is removed."""
 
977
        self.ui.on_tc_browser_notify_load_status(self.browser)
 
978
        self.assertEqual('', self.ui.help_label.get_text())
 
979
 
 
980
    def test_tc_browser_opens_the_proper_url(self):
 
981
        """Terms & Conditions browser shows the proper uri."""
 
982
        self.assertEqual(self.browser.get_property('uri'), TC_URL)
 
983
 
 
984
    def test_notify_load_status_connected(self):
 
985
        """The 'notify::load-status' signal is connected."""
 
986
        expected = [self.ui.on_tc_browser_notify_load_status]
 
987
        self.assertEqual(self.browser._signals['notify::load-status'],
 
988
                         expected)
 
989
 
 
990
    # Unused variable 'skip'
 
991
    # pylint: disable=W0612
 
992
    test_notify_load_status_connected.skip = \
 
993
        'Connecting to notify::load-status makes U1 terms navigation fail.'
 
994
 
 
995
    def test_notify_load_finished_connected(self):
 
996
        """The 'load-finished' signal is connected."""
 
997
        expected = [self.ui.on_tc_browser_notify_load_status]
 
998
        self.assertEqual(self.browser._signals['load-finished'],
 
999
                         expected)
 
1000
 
 
1001
    def test_tc_loaded_morphs_into_tc_browser_vbox(self):
 
1002
        """When the Terms & Conditions is loaded, show the browser window."""
 
1003
        self.ui.on_tc_browser_notify_load_status(self.browser)
 
1004
        self.assert_pages_visibility(tc_browser=True)
 
1005
 
 
1006
    def test_navigation_requested_connected(self):
 
1007
        """The 'navigation-policy-decision-requested' signal is connected."""
 
1008
        actual = self.browser._signals['navigation-policy-decision-requested']
 
1009
        expected = [self.ui.on_tc_browser_navigation_requested]
 
1010
        self.assertEqual(actual, expected)
 
1011
 
 
1012
    def test_navigation_requested_succeeds_for_no_clicking(self):
 
1013
        """The navigation request succeeds when user hasn't clicked a link."""
 
1014
        action = webkit.WebNavigationAction()
 
1015
        action.set_reason(gui.WEBKIT_WEB_NAVIGATION_REASON_OTHER)
 
1016
 
 
1017
        decision = webkit.WebPolicyDecision()
 
1018
        decision.use = self._set_called
 
1019
 
 
1020
        kwargs = dict(browser=self.browser, frame=None, request=None,
 
1021
                      action=action, decision=decision)
 
1022
        self.ui.on_tc_browser_navigation_requested(**kwargs)
 
1023
        self.assertEqual(self._called, ((), {}))
 
1024
 
 
1025
    def test_navigation_requested_ignores_clicked_links(self):
 
1026
        """The navigation request is ignored if a link was clicked."""
 
1027
        action = webkit.WebNavigationAction()
 
1028
        action.set_reason(gui.WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED)
 
1029
 
 
1030
        decision = webkit.WebPolicyDecision()
 
1031
        decision.ignore = self._set_called
 
1032
 
 
1033
        kwargs = dict(browser=self.browser, frame=None, request=None,
 
1034
                      action=action, decision=decision)
 
1035
        self.ui.on_tc_browser_navigation_requested(**kwargs)
 
1036
        self.assertEqual(self._called, ((), {}))
 
1037
 
 
1038
    def test_navigation_requested_ignores_for_none(self):
 
1039
        """The navigation request is ignoref the request if params are None."""
 
1040
        kwargs = dict(browser=None, frame=None, request=None,
 
1041
                      action=None, decision=None)
 
1042
        self.ui.on_tc_browser_navigation_requested(**kwargs)
 
1043
 
 
1044
    def test_navigation_requested_opens_links_when_clicked(self):
 
1045
        """The navigation request is opened on user's default browser
 
1046
 
 
1047
        (If the user opened a link by clicking into it).
 
1048
 
 
1049
        """
 
1050
        url = 'http://something.com/yadda'
 
1051
        action = webkit.WebNavigationAction()
 
1052
        action.set_reason(gui.WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED)
 
1053
        action.set_original_uri(url)
 
1054
 
 
1055
        decision = webkit.WebPolicyDecision()
 
1056
        decision.ignore = gui.NO_OP
 
1057
 
 
1058
        self.patch(gui.webbrowser, 'open', self._set_called)
 
1059
 
 
1060
        kwargs = dict(browser=self.browser, frame=None, request=None,
 
1061
                      action=action, decision=decision)
 
1062
        self.ui.on_tc_browser_navigation_requested(**kwargs)
 
1063
        self.assertEqual(self._called, ((url,), {}))
 
1064
 
 
1065
 
 
1066
class RegistrationErrorTestCase(UbuntuSSOClientTestCase):
 
1067
    """Test suite for the user registration error handling."""
 
1068
 
 
1069
    def setUp(self):
 
1070
        """Init."""
 
1071
        super(RegistrationErrorTestCase, self).setUp()
 
1072
        self.click_join_with_valid_data()
 
1073
 
 
1074
    def test_previous_page_is_shown(self):
 
1075
        """On UserRegistrationError the previous page is shown."""
 
1076
        self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
 
1077
        self.assert_pages_visibility(enter_details=True)
 
1078
 
 
1079
    def test_captcha_is_reloaded(self):
 
1080
        """On UserRegistrationError the captcha is reloaded."""
 
1081
        self.patch(self.ui, '_generate_captcha', self._set_called)
 
1082
        self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
 
1083
        self.assertEqual(self._called, ((), {}))
 
1084
 
 
1085
    def test_warning_label_is_shown(self):
 
1086
        """On UserRegistrationError the warning label is shown."""
 
1087
        self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
 
1088
        self.assert_correct_label_warning(self.ui.warning_label,
 
1089
                                          self.ui.UNKNOWN_ERROR)
 
1090
 
 
1091
    def test_specific_errors_from_backend_are_shown(self):
 
1092
        """Specific errors from backend are used."""
 
1093
        error = {'errtype': 'RegistrationError',
 
1094
                 'message': 'We\'re so doomed.',
 
1095
                 'email': 'Enter a valid e-mail address.',
 
1096
                 'password': 'I don\'t like your password.',
 
1097
                 '__all__': 'Wrong captcha solution.'}
 
1098
 
 
1099
        self.ui.on_user_registration_error(app_name=APP_NAME, error=error)
 
1100
 
 
1101
        expected = '\n'.join((error['__all__'], error['message']))
 
1102
        self.assert_correct_label_warning(self.ui.warning_label, expected)
 
1103
        self.assert_correct_entry_warning(self.ui.email1_entry,
 
1104
                                          error['email'])
 
1105
        self.assert_correct_entry_warning(self.ui.email2_entry,
 
1106
                                          error['email'])
 
1107
        self.assert_correct_entry_warning(self.ui.password1_entry,
 
1108
                                          error['password'])
 
1109
        self.assert_correct_entry_warning(self.ui.password2_entry,
 
1110
                                          error['password'])
 
1111
 
 
1112
 
 
1113
class VerifyEmailTestCase(UbuntuSSOClientTestCase):
 
1114
    """Test suite for the user registration (verify email page)."""
 
1115
 
 
1116
    def setUp(self):
 
1117
        """Init."""
 
1118
        super(VerifyEmailTestCase, self).setUp()
 
1119
        self.ui.on_user_registered(app_name=APP_NAME, email=EMAIL)
 
1120
        self.click_verify_email_with_valid_data()
 
1121
 
 
1122
    def test_registration_successful_shows_verify_email_vbox(self):
 
1123
        """Receiving 'registration_successful' shows the verify email vbox."""
 
1124
        self.ui.on_user_registered(app_name=APP_NAME, email=EMAIL)
 
1125
        self.assert_pages_visibility(verify_email=True)
 
1126
 
 
1127
    def test_help_label_display_correct_wording(self):
 
1128
        """The help_label display VERIFY_EMAIL_LABEL."""
 
1129
        msg = 'help_label must read "%s" (got "%s" instead).'
 
1130
        actual = self.ui.help_label.get_label()
 
1131
        expected = self.ui.VERIFY_EMAIL_LABEL % {'app_name': APP_NAME,
 
1132
                                                 'email': EMAIL}
 
1133
        self.assertEqual(expected, actual, msg % (expected, actual))
 
1134
 
 
1135
    def test_on_verify_token_button_clicked_calls_validate_email(self):
 
1136
        """Verify token button triggers call to backend."""
 
1137
        self.click_verify_email_with_valid_data()
 
1138
        expected = 'validate_email'
 
1139
        self.assertIn(expected, self.ui.backend._called)
 
1140
        self.assertEqual(self.ui.backend._called[expected],
 
1141
                         ((APP_NAME, EMAIL, PASSWORD, EMAIL_TOKEN),
 
1142
                          dict(reply_handler=gui.NO_OP,
 
1143
                               error_handler=gui.NO_OP)))
 
1144
 
 
1145
    def test_on_verify_token_button_clicked(self):
 
1146
        """Verify token uses cached user_email and user_password."""
 
1147
        self.ui.user_email = 'test@me.com'
 
1148
        self.ui.user_password = 'yadda-yedda'
 
1149
        self.ui.on_verify_token_button_clicked()
 
1150
        self.assertEqual(self.ui.backend._called['validate_email'],
 
1151
                         ((APP_NAME, self.ui.user_email,
 
1152
                           self.ui.user_password, EMAIL_TOKEN),
 
1153
                          dict(reply_handler=gui.NO_OP,
 
1154
                               error_handler=gui.NO_OP)))
 
1155
 
 
1156
    def test_on_verify_token_button_shows_processing_page(self):
 
1157
        """Verify token button triggers call to backend."""
 
1158
        self.click_verify_email_with_valid_data()
 
1159
        self.assert_pages_visibility(processing=True)
 
1160
 
 
1161
    def test_no_warning_messages_if_valid_data(self):
 
1162
        """No warning messages are shown if the data is valid."""
 
1163
        # this will certainly NOT generate warnings
 
1164
        self.click_verify_email_with_valid_data()
 
1165
        self.assert_warnings_visibility()
 
1166
 
 
1167
    def test_on_email_validated_shows_processing_page(self):
 
1168
        """On email validated the procesing page is still shown."""
 
1169
        self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
 
1170
        self.assert_pages_visibility(processing=True)
 
1171
 
 
1172
    def test_on_email_validated_does_not_clear_the_help_text(self):
 
1173
        """On email validated the help text is not removed."""
 
1174
        self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
 
1175
        self.assertEqual(self.ui.verify_email_vbox.help_text,
 
1176
                         self.ui.help_label.get_label())
 
1177
 
 
1178
    def test_on_email_validation_error_verify_email_is_shown(self):
 
1179
        """On email validation error, the verify_email page is shown."""
 
1180
        self.ui.on_email_validation_error(app_name=APP_NAME, error=self.error)
 
1181
        self.assert_pages_visibility(verify_email=True)
 
1182
        self.assert_correct_label_warning(self.ui.warning_label,
 
1183
                                          self.ui.UNKNOWN_ERROR)
 
1184
 
 
1185
    def test_specific_errors_from_backend_are_shown(self):
 
1186
        """Specific errors from backend are used."""
 
1187
        error = {'errtype': 'EmailValidationError',
 
1188
                 'message': 'We\'re so doomed.',
 
1189
                 'email_token': 'Enter a valid e-mail address.',
 
1190
                 '__all__': 'We all are gonna die.'}
 
1191
 
 
1192
        self.ui.on_email_validation_error(app_name=APP_NAME, error=error)
 
1193
 
 
1194
        expected = '\n'.join((error['__all__'], error['message']))
 
1195
        self.assert_correct_label_warning(self.ui.warning_label, expected)
 
1196
        self.assert_correct_entry_warning(self.ui.email_token_entry,
 
1197
                                          error['email_token'])
 
1198
 
 
1199
    def test_success_label_is_correct(self):
 
1200
        """The success message is correct."""
 
1201
        self.assertEqual(self.ui.SUCCESS,
 
1202
                         self.ui.success_vbox.label.get_text())
 
1203
        markup = self.ui.success_vbox.label.get_label()
 
1204
        self.assertTrue('<span size="x-large">' in markup)
 
1205
 
 
1206
    def test_error_label_is_correct(self):
 
1207
        """The error message is correct."""
 
1208
        self.assertEqual(self.ui.ERROR,
 
1209
                         self.ui.error_vbox.label.get_text())
 
1210
        markup = self.ui.error_vbox.label.get_label()
 
1211
        self.assertTrue('<span size="x-large">' in markup)
 
1212
 
 
1213
    def test_on_finish_close_button_clicked_closes_window(self):
 
1214
        """When done the window is closed."""
 
1215
        self.ui.finish_close_button.clicked()
 
1216
        self.assertFalse(self.ui.window.get_property('visible'))
 
1217
 
 
1218
    def test_verify_token_button_does_nothing_if_clicked_but_disabled(self):
 
1219
        """The email token can only be submitted if the button is sensitive."""
 
1220
        self.patch(self.ui.email_token_entry, 'get_text', self._set_called)
 
1221
 
 
1222
        self.ui.verify_token_button.set_sensitive(False)
 
1223
        self.ui.verify_token_button.clicked()
 
1224
        self.assertFalse(self._called)
 
1225
 
 
1226
        self.ui.verify_token_button.set_sensitive(True)
 
1227
        self.ui.verify_token_button.clicked()
 
1228
        self.assertTrue(self._called)
 
1229
 
 
1230
 
 
1231
class VerifyEmailValidationTestCase(UbuntuSSOClientTestCase):
 
1232
    """Test suite for the user registration validation (verify email page)."""
 
1233
 
 
1234
    def setUp(self):
 
1235
        """Init."""
 
1236
        super(VerifyEmailValidationTestCase, self).setUp()
 
1237
        self.ui.on_user_registered(app_name=APP_NAME, email=EMAIL)
 
1238
 
 
1239
    def test_warning_is_shown_if_empty_email_token(self):
 
1240
        """A warning message is shown if email token is empty."""
 
1241
        self.ui.email_token_entry.set_text('')
 
1242
 
 
1243
        self.ui.verify_token_button.clicked()
 
1244
 
 
1245
        self.assert_correct_entry_warning(self.ui.email_token_entry,
 
1246
                                          self.ui.FIELD_REQUIRED)
 
1247
        self.assertNotIn('validate_email', self.ui.backend._called)
 
1248
 
 
1249
    def test_no_warning_messages_if_valid_data(self):
 
1250
        """No warning messages are shown if the data is valid."""
 
1251
        # this will certainly NOT generate warnings
 
1252
        self.click_verify_email_with_valid_data()
 
1253
 
 
1254
        self.assert_warnings_visibility()
 
1255
 
 
1256
    def test_no_warning_messages_if_valid_data_after_invalid_data(self):
 
1257
        """No warnings if the data is valid (with prior invalid data)."""
 
1258
        # this will certainly generate warnings
 
1259
        self.ui.verify_token_button.clicked()
 
1260
 
 
1261
        # this will certainly NOT generate warnings
 
1262
        self.click_verify_email_with_valid_data()
 
1263
 
 
1264
        self.assert_warnings_visibility()
 
1265
 
 
1266
 
 
1267
class VerifyEmailLoginOnlyTestCase(VerifyEmailTestCase):
 
1268
    """Test suite for the user login (verify email page)."""
 
1269
 
 
1270
    kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT,
 
1271
                  login_only=True)
 
1272
 
 
1273
 
 
1274
class VerifyEmailValidationLoginOnlyTestCase(VerifyEmailValidationTestCase):
 
1275
    """Test suite for the user login validation (verify email page)."""
 
1276
 
 
1277
    kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT,
 
1278
                  login_only=True)
 
1279
 
 
1280
 
 
1281
class RegistrationValidationTestCase(UbuntuSSOClientTestCase):
 
1282
    """Test suite for the user registration  validations."""
 
1283
 
 
1284
    def setUp(self):
 
1285
        """Init."""
 
1286
        super(RegistrationValidationTestCase, self).setUp()
 
1287
        self.ui.join_ok_button.set_sensitive(True)
 
1288
 
 
1289
    def test_warning_is_shown_if_name_empty(self):
 
1290
        """A warning message is shown if name is empty."""
 
1291
        self.ui.name_entry.set_text('')
 
1292
 
 
1293
        self.ui.join_ok_button.clicked()
 
1294
 
 
1295
        self.assert_correct_entry_warning(self.ui.name_entry,
 
1296
                                          self.ui.FIELD_REQUIRED)
 
1297
        self.assertNotIn('register_user', self.ui.backend._called)
 
1298
 
 
1299
    # Unused variable 'skip'
 
1300
    # pylint: disable=W0612
 
1301
    test_warning_is_shown_if_name_empty.skip = \
 
1302
        'Unused for now, will be hidden to save space (LP: #627440).'
 
1303
 
 
1304
    def test_warning_is_shown_if_empty_email(self):
 
1305
        """A warning message is shown if emails are empty."""
 
1306
        self.ui.email1_entry.set_text('')
 
1307
        self.ui.email2_entry.set_text('')
 
1308
 
 
1309
        self.ui.join_ok_button.clicked()
 
1310
 
 
1311
        self.assert_correct_entry_warning(self.ui.email1_entry,
 
1312
                                          self.ui.FIELD_REQUIRED)
 
1313
        self.assert_correct_entry_warning(self.ui.email2_entry,
 
1314
                                          self.ui.FIELD_REQUIRED)
 
1315
        self.assertNotIn('register_user', self.ui.backend._called)
 
1316
 
 
1317
    def test_warning_is_shown_if_email_mismatch(self):
 
1318
        """A warning message is shown if emails doesn't match."""
 
1319
        self.ui.email1_entry.set_text(EMAIL)
 
1320
        self.ui.email2_entry.set_text(EMAIL * 2)
 
1321
 
 
1322
        self.ui.join_ok_button.clicked()
 
1323
 
 
1324
        self.assert_correct_entry_warning(self.ui.email1_entry,
 
1325
                                          self.ui.EMAIL_MISMATCH)
 
1326
        self.assert_correct_entry_warning(self.ui.email2_entry,
 
1327
                                          self.ui.EMAIL_MISMATCH)
 
1328
        self.assertNotIn('register_user', self.ui.backend._called)
 
1329
 
 
1330
    def test_warning_is_shown_if_invalid_email(self):
 
1331
        """A warning message is shown if email is invalid."""
 
1332
        self.ui.email1_entry.set_text('q')
 
1333
        self.ui.email2_entry.set_text('q')
 
1334
 
 
1335
        self.ui.join_ok_button.clicked()
 
1336
 
 
1337
        self.assert_correct_entry_warning(self.ui.email1_entry,
 
1338
                                          self.ui.EMAIL_INVALID)
 
1339
        self.assert_correct_entry_warning(self.ui.email2_entry,
 
1340
                                          self.ui.EMAIL_INVALID)
 
1341
        self.assertNotIn('register_user', self.ui.backend._called)
 
1342
 
 
1343
    def test_password_help_is_always_shown(self):
 
1344
        """Password help text is correctly displayed."""
 
1345
        self.assertTrue(self.ui.password_help_label.get_property('visible'),
 
1346
                        'password help text is visible.')
 
1347
        self.assertEqual(self.ui.password_help_label.get_text(),
 
1348
                         self.ui.PASSWORD_HELP)
 
1349
        self.assertNotIn('register_user', self.ui.backend._called)
 
1350
 
 
1351
    def test_warning_is_shown_if_password_mismatch(self):
 
1352
        """A warning message is shown if password doesn't match."""
 
1353
        self.ui.password1_entry.set_text(PASSWORD)
 
1354
        self.ui.password2_entry.set_text(PASSWORD * 2)
 
1355
 
 
1356
        self.ui.join_ok_button.clicked()
 
1357
 
 
1358
        self.assert_correct_entry_warning(self.ui.password1_entry,
 
1359
                                          self.ui.PASSWORD_MISMATCH)
 
1360
        self.assert_correct_entry_warning(self.ui.password2_entry,
 
1361
                                          self.ui.PASSWORD_MISMATCH)
 
1362
        self.assertNotIn('register_user', self.ui.backend._called)
 
1363
 
 
1364
    def test_warning_is_shown_if_password_too_weak(self):
 
1365
        """A warning message is shown if password is too weak."""
 
1366
        # password will match but will be too weak
 
1367
        for pwd in ('', 'h3lloWo', PASSWORD.lower(), 'helloWorld'):
 
1368
            self.ui.password1_entry.set_text(pwd)
 
1369
            self.ui.password2_entry.set_text(pwd)
 
1370
 
 
1371
            self.ui.join_ok_button.clicked()
 
1372
 
 
1373
            self.assert_correct_entry_warning(self.ui.password1_entry,
 
1374
                                              self.ui.PASSWORD_TOO_WEAK)
 
1375
            self.assert_correct_entry_warning(self.ui.password2_entry,
 
1376
                                              self.ui.PASSWORD_TOO_WEAK)
 
1377
        self.assertNotIn('register_user', self.ui.backend._called)
 
1378
 
 
1379
    def test_warning_is_shown_if_tc_not_accepted(self):
 
1380
        """A warning message is shown if TC are not accepted."""
 
1381
        # don't agree to TC
 
1382
        self.ui.yes_to_tc_checkbutton.set_active(False)
 
1383
 
 
1384
        self.ui.join_ok_button.clicked()
 
1385
 
 
1386
        self.assert_correct_label_warning(self.ui.tc_warning_label,
 
1387
                                          self.ui.TC_NOT_ACCEPTED)
 
1388
        self.assertNotIn('register_user', self.ui.backend._called)
 
1389
 
 
1390
    def test_warning_is_shown_if_not_captcha_solution(self):
 
1391
        """A warning message is shown if TC are not accepted."""
 
1392
        # captcha solution will be empty
 
1393
        self.ui.captcha_solution_entry.set_text('')
 
1394
 
 
1395
        self.ui.join_ok_button.clicked()
 
1396
 
 
1397
        self.assert_correct_entry_warning(self.ui.captcha_solution_entry,
 
1398
                                          self.ui.FIELD_REQUIRED)
 
1399
        self.assertNotIn('register_user', self.ui.backend._called)
 
1400
 
 
1401
    def test_no_warning_messages_if_valid_data(self):
 
1402
        """No warning messages are shown if the data is valid."""
 
1403
        # this will certainly NOT generate warnings
 
1404
        self.click_join_with_valid_data()
 
1405
 
 
1406
        self.assert_warnings_visibility()
 
1407
 
 
1408
    def test_no_warning_messages_if_valid_data_after_invalid_data(self):
 
1409
        """No warnings if the data is valid (with prior invalid data)."""
 
1410
        # this will certainly generate warnings
 
1411
        self.ui.join_ok_button.clicked()
 
1412
 
 
1413
        # this will certainly NOT generate warnings
 
1414
        self.click_join_with_valid_data()
 
1415
 
 
1416
        self.assert_warnings_visibility()
 
1417
 
 
1418
 
 
1419
class LoginTestCase(UbuntuSSOClientTestCase):
 
1420
    """Test suite for the user login pages."""
 
1421
 
 
1422
    def setUp(self):
 
1423
        """Init."""
 
1424
        super(LoginTestCase, self).setUp()
 
1425
        self.ui.login_button.clicked()
 
1426
 
 
1427
    def test_login_button_clicked_morphs_to_login_page(self):
 
1428
        """Clicking sig_in_button morphs window into login page."""
 
1429
        self.assert_pages_visibility(login=True)
 
1430
 
 
1431
    def test_initial_text_for_header_label(self):
 
1432
        """The header must have the correct text when logging in."""
 
1433
        msg = 'Text for the header must be "%s" (got "%s" instead).'
 
1434
        expected = self.ui.LOGIN_HEADER_LABEL % {'app_name': APP_NAME}
 
1435
        actual = self.ui.header_label.get_text()
 
1436
        self.assertEqual(expected, actual, msg % (expected, actual))
 
1437
 
 
1438
    def test_initial_text_for_help_label(self):
 
1439
        """The help must have the correct text at startup."""
 
1440
        msg = 'Text for the help must be "%s" (got "%s" instead).'
 
1441
        expected = self.ui.CONNECT_HELP_LABEL % {'app_name': APP_NAME}
 
1442
        actual = self.ui.help_label.get_text()
 
1443
        self.assertEqual(expected, actual, msg % (expected, actual))
 
1444
 
 
1445
    def test_entries_are_packed_to_ui_for_login(self):
 
1446
        """Every entry is properly packed in the ui for the login page."""
 
1447
        entries = ('login_email', 'login_password')
 
1448
        self.assert_entries_are_packed_to_ui('login_details_vbox', entries)
 
1449
 
 
1450
    def test_entries_are_packed_to_ui_for_set_new_password(self):
 
1451
        """Every entry is packed in the ui for the reset password page."""
 
1452
        entries = ('reset_code', 'reset_password1', 'reset_password2')
 
1453
        self.assert_entries_are_packed_to_ui('set_new_password_details_vbox',
 
1454
                                             entries)
 
1455
 
 
1456
    def test_entries_are_packed_to_ui_for_request_password_token(self):
 
1457
        """Every entry is packed in the ui for the reset email page."""
 
1458
        container_name = 'request_password_token_details_vbox'
 
1459
        entries = ('reset_email',)
 
1460
        self.assert_entries_are_packed_to_ui(container_name, entries)
 
1461
 
 
1462
    def test_on_login_back_button_clicked(self):
 
1463
        """Clicking login_back_button show registration page."""
 
1464
        self.ui.login_back_button.clicked()
 
1465
        self.assert_pages_visibility(enter_details=True)
 
1466
 
 
1467
    def test_on_login_connect_button_clicked(self):
 
1468
        """Clicking login_ok_button calls backend.login."""
 
1469
        self.click_connect_with_valid_data()
 
1470
 
 
1471
        expected = 'login'
 
1472
        self.assertIn(expected, self.ui.backend._called)
 
1473
        self.assertEqual(self.ui.backend._called[expected],
 
1474
                         ((APP_NAME, EMAIL, PASSWORD),
 
1475
                          dict(reply_handler=gui.NO_OP,
 
1476
                               error_handler=gui.NO_OP)))
 
1477
 
 
1478
    def test_on_login_connect_button_clicked_morphs_to_processing_page(self):
 
1479
        """Clicking login_ok_button morphs to the processing page."""
 
1480
        self.click_connect_with_valid_data()
 
1481
        self.assert_pages_visibility(processing=True)
 
1482
 
 
1483
    def test_on_logged_in_morphs_to_processing_page(self):
 
1484
        """When user logged in the processing page is still shown."""
 
1485
        self.click_connect_with_valid_data()
 
1486
        self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
 
1487
        self.assert_pages_visibility(processing=True)
 
1488
 
 
1489
    def test_on_login_error_morphs_to_login_page(self):
 
1490
        """On user login error, the previous page is shown."""
 
1491
        self.click_connect_with_valid_data()
 
1492
        self.ui.on_login_error(app_name=APP_NAME, error=self.error)
 
1493
        self.assert_pages_visibility(login=True)
 
1494
 
 
1495
    def test_on_user_not_validated_morphs_to_verify_page(self):
 
1496
        """On user not validated, the verify page is shown."""
 
1497
        self.click_connect_with_valid_data()
 
1498
        self.ui.on_user_not_validated(app_name=APP_NAME, email=EMAIL)
 
1499
        self.assert_pages_visibility(verify_email=True)
 
1500
 
 
1501
    def test_on_login_error_a_warning_is_shown(self):
 
1502
        """On user login error, a warning is shown with proper wording."""
 
1503
        self.click_connect_with_valid_data()
 
1504
        self.ui.on_login_error(app_name=APP_NAME, error=self.error)
 
1505
        self.assert_correct_label_warning(self.ui.warning_label,
 
1506
                                          self.ui.UNKNOWN_ERROR)
 
1507
 
 
1508
    def test_specific_errors_from_backend_are_shown(self):
 
1509
        """Specific errors from backend are used."""
 
1510
        error = {'errtype': 'AuthenticationError',
 
1511
                 'message': 'We\'re so doomed.',
 
1512
                 '__all__': 'We all are gonna die.'}
 
1513
 
 
1514
        self.ui.on_login_error(app_name=APP_NAME, error=error)
 
1515
 
 
1516
        expected = '\n'.join((error['__all__'], error['message']))
 
1517
        self.assert_correct_label_warning(self.ui.warning_label, expected)
 
1518
 
 
1519
    def test_back_to_registration_hides_warning(self):
 
1520
        """After user login error, warning is hidden when clicking 'Back'."""
 
1521
        self.click_connect_with_valid_data()
 
1522
        self.ui.on_login_error(app_name=APP_NAME, error=self.error)
 
1523
        self.ui.login_back_button.clicked()
 
1524
        self.assert_warnings_visibility()
 
1525
 
 
1526
    def test_login_ok_button_does_nothing_if_clicked_but_disabled(self):
 
1527
        """The join form can only be submitted if the button is sensitive."""
 
1528
        self.patch(self.ui.login_email_entry, 'get_text', self._set_called)
 
1529
 
 
1530
        self.ui.login_ok_button.set_sensitive(False)
 
1531
        self.ui.login_ok_button.clicked()
 
1532
        self.assertFalse(self._called)
 
1533
 
 
1534
        self.ui.login_ok_button.set_sensitive(True)
 
1535
        self.ui.login_ok_button.clicked()
 
1536
        self.assertTrue(self._called)
 
1537
 
 
1538
    def test_user_and_pass_are_cached(self):
 
1539
        """Username and password are temporarly cached for further use."""
 
1540
        self.click_connect_with_valid_data()
 
1541
        self.assertEqual(self.ui.user_email, EMAIL)
 
1542
        self.assertEqual(self.ui.user_password, PASSWORD)
 
1543
 
 
1544
 
 
1545
class LoginValidationTestCase(UbuntuSSOClientTestCase):
 
1546
    """Test suite for the user login validation."""
 
1547
 
 
1548
    def setUp(self):
 
1549
        """Init."""
 
1550
        super(LoginValidationTestCase, self).setUp()
 
1551
        self.ui.login_button.clicked()
 
1552
 
 
1553
    def test_warning_is_shown_if_empty_email(self):
 
1554
        """A warning message is shown if email is empty."""
 
1555
        self.ui.login_email_entry.set_text('')
 
1556
 
 
1557
        self.ui.login_ok_button.clicked()
 
1558
 
 
1559
        self.assert_correct_entry_warning(self.ui.login_email_entry,
 
1560
                                          self.ui.FIELD_REQUIRED)
 
1561
        self.assertNotIn('login', self.ui.backend._called)
 
1562
 
 
1563
    def test_warning_is_shown_if_invalid_email(self):
 
1564
        """A warning message is shown if email is invalid."""
 
1565
        self.ui.login_email_entry.set_text('q')
 
1566
 
 
1567
        self.ui.login_ok_button.clicked()
 
1568
 
 
1569
        self.assert_correct_entry_warning(self.ui.login_email_entry,
 
1570
                                          self.ui.EMAIL_INVALID)
 
1571
        self.assertNotIn('login', self.ui.backend._called)
 
1572
 
 
1573
    def test_warning_is_shown_if_empty_password(self):
 
1574
        """A warning message is shown if password is empty."""
 
1575
        self.ui.login_password_entry.set_text('')
 
1576
 
 
1577
        self.ui.login_ok_button.clicked()
 
1578
 
 
1579
        self.assert_correct_entry_warning(self.ui.login_password_entry,
 
1580
                                          self.ui.FIELD_REQUIRED)
 
1581
        self.assertNotIn('login', self.ui.backend._called)
 
1582
 
 
1583
    def test_no_warning_messages_if_valid_data(self):
 
1584
        """No warning messages are shown if the data is valid."""
 
1585
        # this will certainly NOT generate warnings
 
1586
        self.click_connect_with_valid_data()
 
1587
 
 
1588
        self.assert_warnings_visibility()
 
1589
 
 
1590
    def test_no_warning_messages_if_valid_data_after_invalid_data(self):
 
1591
        """No warnings if the data is valid (with prior invalid data)."""
 
1592
        # this will certainly generate warnings
 
1593
        self.ui.login_ok_button.clicked()
 
1594
 
 
1595
        # this will certainly NOT generate warnings
 
1596
        self.click_connect_with_valid_data()
 
1597
 
 
1598
        self.assert_warnings_visibility()
 
1599
 
 
1600
 
 
1601
class ResetPasswordTestCase(UbuntuSSOClientTestCase):
 
1602
    """Test suite for the reset password functionality."""
 
1603
 
 
1604
    def setUp(self):
 
1605
        """Init."""
 
1606
        super(ResetPasswordTestCase, self).setUp()
 
1607
        self.ui.login_button.clicked()
 
1608
        self.ui.forgotten_password_button.clicked()
 
1609
 
 
1610
    def test_forgotten_password_button_has_the_proper_wording(self):
 
1611
        """The forgotten_password_button has the proper wording."""
 
1612
        self.assertEqual(self.ui.forgotten_password_button.get_label(),
 
1613
                         self.ui.FORGOTTEN_PASSWORD_BUTTON)
 
1614
 
 
1615
    def test_on_forgotten_password_button_clicked_help_text(self):
 
1616
        """Clicking forgotten_password_button the help is properly changed."""
 
1617
        wanted = self.ui.REQUEST_PASSWORD_TOKEN_LABEL % {'app_name': APP_NAME}
 
1618
        self.assertEqual(self.ui.help_label.get_text(), wanted)
 
1619
 
 
1620
    def test_on_forgotten_password_button_clicked_header_label(self):
 
1621
        """Clicking forgotten_password_button the title is properly changed."""
 
1622
        self.assertEqual(self.ui.header_label.get_text(),
 
1623
                         self.ui.RESET_PASSWORD)
 
1624
 
 
1625
    def test_on_forgotten_password_button_clicked_ok_button(self):
 
1626
        """Clicking forgotten_password_button the ok button reads 'Next'."""
 
1627
        self.assertEqual(self.ui.request_password_token_ok_button.get_label(),
 
1628
                         self.ui.NEXT)
 
1629
 
 
1630
    def test_on_forgotten_password_button_clicked_morphs_window(self):
 
1631
        """Clicking forgotten_password_button the proper page is shown."""
 
1632
        self.assert_pages_visibility(request_password_token=True)
 
1633
 
 
1634
    def test_on_request_password_token_back_button_clicked(self):
 
1635
        """Clicking request_password_token_back_button show login screen."""
 
1636
        self.ui.request_password_token_back_button.clicked()
 
1637
        self.assert_pages_visibility(login=True)
 
1638
 
 
1639
    def test_request_password_token_ok_button_disabled_until_email_added(self):
 
1640
        """The button is disabled until email added."""
 
1641
        is_sensitive = self.ui.request_password_token_ok_button.get_sensitive
 
1642
        self.assertFalse(is_sensitive())
 
1643
 
 
1644
        self.ui.reset_email_entry.set_text('a')
 
1645
        self.assertTrue(is_sensitive())
 
1646
 
 
1647
        self.ui.reset_email_entry.set_text('')
 
1648
        self.assertFalse(is_sensitive())
 
1649
 
 
1650
        self.ui.reset_email_entry.set_text('         ')
 
1651
        self.assertFalse(is_sensitive())
 
1652
 
 
1653
    def test_on_request_password_token_ok_button_clicked_morphs_window(self):
 
1654
        """Clicking request_password_token_ok_button morphs processing page."""
 
1655
        self.click_request_password_token_with_valid_data()
 
1656
        self.assert_pages_visibility(processing=True)
 
1657
 
 
1658
    def test_on_request_password_token_ok_button_clicked_calls_backend(self):
 
1659
        """Clicking request_password_token_ok_button the backend is called."""
 
1660
        self.click_request_password_token_with_valid_data()
 
1661
        expected = 'request_password_reset_token'
 
1662
        self.assertIn(expected, self.ui.backend._called)
 
1663
        self.assertEqual(self.ui.backend._called[expected],
 
1664
                         ((APP_NAME, EMAIL),
 
1665
                          dict(reply_handler=gui.NO_OP,
 
1666
                               error_handler=gui.NO_OP)))
 
1667
 
 
1668
    def test_on_password_reset_token_sent_morphs_window(self):
 
1669
        """When the reset token was sent, the reset password page is shown."""
 
1670
        self.click_request_password_token_with_valid_data()
 
1671
        self.ui.on_password_reset_token_sent(app_name=APP_NAME, email=EMAIL)
 
1672
        self.assert_pages_visibility(set_new_password=True)
 
1673
 
 
1674
    def test_on_password_reset_token_sent_help_text(self):
 
1675
        """Clicking request_password_token_ok_button changes the help text."""
 
1676
        self.click_request_password_token_with_valid_data()
 
1677
        self.ui.on_password_reset_token_sent(app_name=APP_NAME, email=EMAIL)
 
1678
 
 
1679
        self.assertEqual(self.ui.help_label.get_text(),
 
1680
                         self.ui.SET_NEW_PASSWORD_LABEL % {'email': EMAIL})
 
1681
 
 
1682
    def test_on_password_reset_token_sent_ok_button(self):
 
1683
        """After request_password_token_ok_button the ok button is updated."""
 
1684
        self.click_request_password_token_with_valid_data()
 
1685
        self.ui.on_password_reset_token_sent(app_name=APP_NAME, email=EMAIL)
 
1686
 
 
1687
        self.assertEqual(self.ui.set_new_password_ok_button.get_label(),
 
1688
                         self.ui.RESET_PASSWORD)
 
1689
 
 
1690
    def test_on_password_reset_error_shows_login_page(self):
 
1691
        """When reset token wasn't successfuly sent the login page is shown."""
 
1692
        self.ui.on_password_reset_error(app_name=APP_NAME, error=self.error)
 
1693
        self.assert_correct_label_warning(self.ui.warning_label,
 
1694
                                          self.ui.UNKNOWN_ERROR)
 
1695
        self.assert_pages_visibility(login=True)
 
1696
 
 
1697
    def test_specific_errors_from_backend_are_shown(self):
 
1698
        """Specific errors from backend are used."""
 
1699
        error = {'errtype': 'ResetPasswordTokenError',
 
1700
                 'message': 'We\'re so doomed.',
 
1701
                 '__all__': 'We all are gonna die.'}
 
1702
 
 
1703
        self.ui.on_password_reset_error(app_name=APP_NAME, error=error)
 
1704
 
 
1705
        expected = '\n'.join((error['__all__'], error['message']))
 
1706
        self.assert_correct_label_warning(self.ui.warning_label, expected)
 
1707
 
 
1708
    def test_ok_button_does_nothing_if_clicked_but_disabled(self):
 
1709
        """The password token can be requested if the button is sensitive."""
 
1710
        self.patch(self.ui.reset_email_entry, 'get_text', self._set_called)
 
1711
 
 
1712
        self.ui.request_password_token_ok_button.set_sensitive(False)
 
1713
        self.ui.request_password_token_ok_button.clicked()
 
1714
        self.assertFalse(self._called)
 
1715
 
 
1716
        self.ui.request_password_token_ok_button.set_sensitive(True)
 
1717
        self.ui.request_password_token_ok_button.clicked()
 
1718
        self.assertTrue(self._called)
 
1719
 
 
1720
 
 
1721
class ResetPasswordValidationTestCase(UbuntuSSOClientTestCase):
 
1722
    """Test suite for the password reset validations."""
 
1723
 
 
1724
    def test_warning_is_shown_if_empty_email(self):
 
1725
        """A warning message is shown if emails are empty."""
 
1726
        self.ui.reset_email_entry.set_text(' ')
 
1727
 
 
1728
        self.ui.request_password_token_ok_button.set_sensitive(True)
 
1729
        self.ui.request_password_token_ok_button.clicked()
 
1730
 
 
1731
        self.assert_correct_entry_warning(self.ui.reset_email_entry,
 
1732
                                          self.ui.FIELD_REQUIRED)
 
1733
        self.assertNotIn('request_password_reset_token',
 
1734
                         self.ui.backend._called)
 
1735
 
 
1736
    def test_warning_is_shown_if_invalid_email(self):
 
1737
        """A warning message is shown if email is invalid."""
 
1738
        self.ui.reset_email_entry.set_text('q')
 
1739
 
 
1740
        self.ui.request_password_token_ok_button.clicked()
 
1741
 
 
1742
        self.assert_correct_entry_warning(self.ui.reset_email_entry,
 
1743
                                          self.ui.EMAIL_INVALID)
 
1744
        self.assertNotIn('request_password_reset_token',
 
1745
                         self.ui.backend._called)
 
1746
 
 
1747
    def test_no_warning_messages_if_valid_data(self):
 
1748
        """No warning messages are shown if the data is valid."""
 
1749
        # this will certainly NOT generate warnings
 
1750
        self.click_request_password_token_with_valid_data()
 
1751
 
 
1752
        self.assert_warnings_visibility()
 
1753
 
 
1754
    def test_no_warning_messages_if_valid_data_after_invalid_data(self):
 
1755
        """No warnings if the data is valid (with prior invalid data)."""
 
1756
        # this will certainly generate warnings
 
1757
        self.ui.request_password_token_ok_button.clicked()
 
1758
 
 
1759
        # this will certainly NOT generate warnings
 
1760
        self.click_request_password_token_with_valid_data()
 
1761
 
 
1762
        self.assert_warnings_visibility()
 
1763
 
 
1764
 
 
1765
class SetNewPasswordTestCase(UbuntuSSOClientTestCase):
 
1766
    """Test suite for setting a new password functionality."""
 
1767
 
 
1768
    def setUp(self):
 
1769
        """Init."""
 
1770
        super(SetNewPasswordTestCase, self).setUp()
 
1771
        self.click_request_password_token_with_valid_data()
 
1772
        self.ui.on_password_reset_token_sent(app_name=APP_NAME, email=EMAIL)
 
1773
 
 
1774
    def test_on_set_new_password_ok_button_disabled(self):
 
1775
        """The set_new_password_ok_button is disabled until values added."""
 
1776
        self.click_request_password_token_with_valid_data()
 
1777
        self.assertFalse(self.ui.set_new_password_ok_button.get_sensitive())
 
1778
 
 
1779
        msg = 'set_new_password_ok_button must be sensitive (%s) for %r.'
 
1780
        entries = (self.ui.reset_code_entry,
 
1781
                   self.ui.reset_password1_entry,
 
1782
                   self.ui.reset_password2_entry)
 
1783
        for values in itertools.product(('', ' ', 'a'), repeat=3):
 
1784
            expected = True
 
1785
            for entry, val in zip(entries, values):
 
1786
                entry.set_text(val)
 
1787
                expected &= bool(val and not val.isspace())
 
1788
 
 
1789
            actual = self.ui.set_new_password_ok_button.get_sensitive()
 
1790
            self.assertEqual(expected, actual, msg % (expected, values))
 
1791
 
 
1792
    def test_on_set_new_password_ok_button_clicked_morphs_window(self):
 
1793
        """Clicking set_new_password_ok_button the processing page is shown."""
 
1794
        self.click_set_new_password_with_valid_data()
 
1795
        self.assert_pages_visibility(processing=True)
 
1796
 
 
1797
    def test_on_set_new_password_ok_button_clicked_calls_backend(self):
 
1798
        """Clicking set_new_password_ok_button the backend is called."""
 
1799
        self.click_set_new_password_with_valid_data()
 
1800
        expected = 'set_new_password'
 
1801
        self.assertIn(expected, self.ui.backend._called)
 
1802
        self.assertEqual(self.ui.backend._called[expected],
 
1803
                         ((APP_NAME, EMAIL, RESET_PASSWORD_TOKEN, PASSWORD),
 
1804
                          dict(reply_handler=gui.NO_OP,
 
1805
                               error_handler=gui.NO_OP)))
 
1806
 
 
1807
    def test_on_password_changed_shows_login_page(self):
 
1808
        """When password was successfuly changed the login page is shown."""
 
1809
        self.ui.on_password_changed(app_name=APP_NAME, email=EMAIL)
 
1810
        self.assert_correct_label_warning(self.ui.warning_label,
 
1811
                                          self.ui.PASSWORD_CHANGED)
 
1812
        self.assert_pages_visibility(login=True)
 
1813
 
 
1814
    def test_on_password_change_error_shows_login_page(self):
 
1815
        """When password wasn't changed the reset password page is shown."""
 
1816
        self.ui.on_password_change_error(app_name=APP_NAME, error=self.error)
 
1817
        self.assert_correct_label_warning(self.ui.warning_label,
 
1818
                                          self.ui.UNKNOWN_ERROR)
 
1819
        self.assert_pages_visibility(request_password_token=True)
 
1820
 
 
1821
    def test_specific_errors_from_backend_are_shown(self):
 
1822
        """Specific errors from backend are used."""
 
1823
        error = {'errtype': 'NewPasswordError',
 
1824
                 'message': 'We\'re so doomed.',
 
1825
                 '__all__': 'We all are gonna die.'}
 
1826
 
 
1827
        self.ui.on_password_change_error(app_name=APP_NAME, error=error)
 
1828
 
 
1829
        expected = '\n'.join((error['__all__'], error['message']))
 
1830
        self.assert_correct_label_warning(self.ui.warning_label, expected)
 
1831
 
 
1832
    def test_ok_button_does_nothing_if_clicked_but_disabled(self):
 
1833
        """The new passwrd can only be set if the button is sensitive."""
 
1834
        self.patch(self.ui.reset_code_entry, 'get_text', self._set_called)
 
1835
 
 
1836
        self.ui.set_new_password_ok_button.set_sensitive(False)
 
1837
        self.ui.set_new_password_ok_button.clicked()
 
1838
        self.assertFalse(self._called)
 
1839
 
 
1840
        self.ui.set_new_password_ok_button.set_sensitive(True)
 
1841
        self.ui.set_new_password_ok_button.clicked()
 
1842
        self.assertTrue(self._called)
 
1843
 
 
1844
 
 
1845
class SetNewPasswordValidationTestCase(UbuntuSSOClientTestCase):
 
1846
    """Test suite for validations for setting a new password."""
 
1847
 
 
1848
    def test_warning_is_shown_if_reset_code_empty(self):
 
1849
        """A warning message is shown if reset_code is empty."""
 
1850
        self.ui.reset_code_entry.set_text('')
 
1851
 
 
1852
        self.ui.set_new_password_ok_button.set_sensitive(True)
 
1853
        self.ui.set_new_password_ok_button.clicked()
 
1854
 
 
1855
        self.assert_correct_entry_warning(self.ui.reset_code_entry,
 
1856
                                          self.ui.FIELD_REQUIRED)
 
1857
        self.assertNotIn('set_new_password', self.ui.backend._called)
 
1858
 
 
1859
    def test_password_help_is_always_shown(self):
 
1860
        """Password help text is correctly displayed."""
 
1861
        visible = self.ui.reset_password_help_label.get_property('visible')
 
1862
        self.assertTrue(visible, 'password help text is visible.')
 
1863
        self.assertEqual(self.ui.reset_password_help_label.get_text(),
 
1864
                         self.ui.PASSWORD_HELP)
 
1865
        self.assertNotIn('set_new_password', self.ui.backend._called)
 
1866
 
 
1867
    def test_warning_is_shown_if_password_mismatch(self):
 
1868
        """A warning message is shown if password doesn't match."""
 
1869
        self.ui.reset_password1_entry.set_text(PASSWORD)
 
1870
        self.ui.reset_password2_entry.set_text(PASSWORD * 2)
 
1871
 
 
1872
        self.ui.set_new_password_ok_button.set_sensitive(True)
 
1873
        self.ui.set_new_password_ok_button.clicked()
 
1874
 
 
1875
        self.assert_correct_entry_warning(self.ui.reset_password1_entry,
 
1876
                                          self.ui.PASSWORD_MISMATCH)
 
1877
        self.assert_correct_entry_warning(self.ui.reset_password2_entry,
 
1878
                                          self.ui.PASSWORD_MISMATCH)
 
1879
        self.assertNotIn('set_new_password', self.ui.backend._called)
 
1880
 
 
1881
    def test_warning_is_shown_if_password_too_weak(self):
 
1882
        """A warning message is shown if password is too weak."""
 
1883
        # password will match but will be too weak
 
1884
        for pwd in ('', 'h3lloWo', PASSWORD.lower(), 'helloWorld'):
 
1885
            self.ui.reset_password1_entry.set_text(pwd)
 
1886
            self.ui.reset_password2_entry.set_text(pwd)
 
1887
 
 
1888
            self.ui.set_new_password_ok_button.set_sensitive(True)
 
1889
            self.ui.set_new_password_ok_button.clicked()
 
1890
 
 
1891
            self.assert_correct_entry_warning(self.ui.reset_password1_entry,
 
1892
                                              self.ui.PASSWORD_TOO_WEAK)
 
1893
            self.assert_correct_entry_warning(self.ui.reset_password2_entry,
 
1894
                                              self.ui.PASSWORD_TOO_WEAK)
 
1895
        self.assertNotIn('set_new_password', self.ui.backend._called)
 
1896
 
 
1897
    def test_no_warning_messages_if_valid_data(self):
 
1898
        """No warning messages are shown if the data is valid."""
 
1899
        # this will certainly NOT generate warnings
 
1900
        self.click_set_new_password_with_valid_data()
 
1901
 
 
1902
        self.assert_warnings_visibility()
 
1903
 
 
1904
    def test_no_warning_messages_if_valid_data_after_invalid_data(self):
 
1905
        """No warnings if the data is valid (with prior invalid data)."""
 
1906
        # this will certainly generate warnings
 
1907
        self.ui.set_new_password_ok_button.clicked()
 
1908
 
 
1909
        # this will certainly NOT generate warnings
 
1910
        self.click_set_new_password_with_valid_data()
 
1911
 
 
1912
        self.assert_warnings_visibility()
 
1913
 
 
1914
 
 
1915
class DbusTestCase(UbuntuSSOClientTestCase):
 
1916
    """Test suite for the dbus calls."""
 
1917
 
 
1918
    def test_all_the_signals_are_listed(self):
 
1919
        """All the backend signals are listed to be binded."""
 
1920
        for sig in ('CaptchaGenerated', 'CaptchaGenerationError',
 
1921
                    'UserRegistered', 'UserRegistrationError',
 
1922
                    'LoggedIn', 'LoginError', 'UserNotValidated',
 
1923
                    'EmailValidated', 'EmailValidationError',
 
1924
                    'PasswordResetTokenSent', 'PasswordResetError',
 
1925
                    'PasswordChanged', 'PasswordChangeError'):
 
1926
            self.assertIn(sig, self.ui._signals)
 
1927
 
 
1928
    def test_signal_receivers_are_connected(self):
 
1929
        """Callbacks are connected to signals of interest."""
 
1930
        msg1 = 'callback %r for signal %r must be added to the internal bus.'
 
1931
        msg2 = 'callback %r for signal %r must be added to the ui log.'
 
1932
        dbus_iface = self.ui.iface_name
 
1933
        for signal, method in self.ui._signals.iteritems():
 
1934
            actual = self.ui.bus.callbacks.get((dbus_iface, signal))
 
1935
            self.assertEqual(method, actual, msg1 % (method, signal))
 
1936
            actual = self.ui._signals_receivers.get((dbus_iface, signal))
 
1937
            self.assertEqual(method, actual, msg2 % (method, signal))
 
1938
 
 
1939
    def test_callbacks_only_log_when_app_name_doesnt_match(self):
 
1940
        """Callbacks do nothing but logging when app_name doesn't match."""
 
1941
        mismatch_app_name = self.ui.app_name * 2
 
1942
        for method in self.ui._signals.itervalues():
 
1943
            msgs = ('ignoring', method.__name__, mismatch_app_name)
 
1944
            method(mismatch_app_name, 'dummy')
 
1945
            self.assertTrue(self.memento.check(logging.INFO, *msgs))
 
1946
            self.memento.records = []
 
1947
 
 
1948
    def test_on_captcha_generated_is_not_called(self):
 
1949
        """on_captcha_generated is not called if incorrect app_name."""
 
1950
        self.patch(self.ui, 'on_captcha_generated', self._set_called)
 
1951
        mismatch_app_name = self.ui.app_name * 2
 
1952
        self.ui._signals['CaptchaGenerated'](mismatch_app_name, 'dummy')
 
1953
        self.assertFalse(self._called)
 
1954
 
 
1955
    def test_on_captcha_generation_error_is_not_called(self):
 
1956
        """on_captcha_generation_error is not called if incorrect app_name."""
 
1957
        self.patch(self.ui, 'on_captcha_generation_error', self._set_called)
 
1958
        mismatch_app_name = self.ui.app_name * 2
 
1959
        self.ui._signals['CaptchaGenerationError'](mismatch_app_name, 'dummy')
 
1960
        self.assertFalse(self._called)
 
1961
 
 
1962
    def test_on_user_registered_is_not_called(self):
 
1963
        """on_user_registered is not called if incorrect app_name."""
 
1964
        self.patch(self.ui, 'on_user_registered', self._set_called)
 
1965
        mismatch_app_name = self.ui.app_name * 2
 
1966
        self.ui._signals['UserRegistered'](mismatch_app_name, 'dummy')
 
1967
        self.assertFalse(self._called)
 
1968
 
 
1969
    def test_on_user_registration_error_is_not_called(self):
 
1970
        """on_user_registration_error is not called if incorrect app_name."""
 
1971
        self.patch(self.ui, 'on_user_registration_error', self._set_called)
 
1972
        mismatch_app_name = self.ui.app_name * 2
 
1973
        self.ui._signals['UserRegistrationError'](mismatch_app_name, 'dummy')
 
1974
        self.assertFalse(self._called)
 
1975
 
 
1976
    def test_on_email_validated_is_not_called(self):
 
1977
        """on_email_validated is not called if incorrect app_name."""
 
1978
        self.patch(self.ui, 'on_email_validated', self._set_called)
 
1979
        mismatch_app_name = self.ui.app_name * 2
 
1980
        self.ui._signals['EmailValidated'](mismatch_app_name, 'dummy')
 
1981
        self.assertFalse(self._called)
 
1982
 
 
1983
    def test_on_email_validation_error_is_not_called(self):
 
1984
        """on_email_validation_error is not called if incorrect app_name."""
 
1985
        self.patch(self.ui, 'on_email_validation_error', self._set_called)
 
1986
        mismatch_app_name = self.ui.app_name * 2
 
1987
        self.ui._signals['EmailValidationError'](mismatch_app_name, 'dummy')
 
1988
        self.assertFalse(self._called)
 
1989
 
 
1990
    def test_on_logged_in_is_not_called(self):
 
1991
        """on_logged_in is not called if incorrect app_name."""
 
1992
        self.patch(self.ui, 'on_logged_in', self._set_called)
 
1993
        mismatch_app_name = self.ui.app_name * 2
 
1994
        self.ui._signals['LoggedIn'](mismatch_app_name, 'dummy')
 
1995
        self.assertFalse(self._called)
 
1996
 
 
1997
    def test_on_login_error_is_not_called(self):
 
1998
        """on_login_error is not called if incorrect app_name."""
 
1999
        self.patch(self.ui, 'on_login_error', self._set_called)
 
2000
        mismatch_app_name = self.ui.app_name * 2
 
2001
        self.ui._signals['LoginError'](mismatch_app_name, 'dummy')
 
2002
        self.assertFalse(self._called)
 
2003
 
 
2004
    def test_on_user_not_validated_is_not_called(self):
 
2005
        """on_user_not_validated is not called if incorrect app_name."""
 
2006
        self.patch(self.ui, 'on_user_not_validated', self._set_called)
 
2007
        mismatch_app_name = self.ui.app_name * 2
 
2008
        self.ui._signals['UserNotValidated'](mismatch_app_name, 'dummy')
 
2009
        self.assertFalse(self._called)
 
2010
 
 
2011
    def test_on_password_reset_token_sent_is_not_called(self):
 
2012
        """on_password_reset_token_sent is not called if incorrect app_name."""
 
2013
        self.patch(self.ui, 'on_password_reset_token_sent', self._set_called)
 
2014
        mismatch_app_name = self.ui.app_name * 2
 
2015
        self.ui._signals['PasswordResetTokenSent'](mismatch_app_name, 'dummy')
 
2016
        self.assertFalse(self._called)
 
2017
 
 
2018
    def test_on_password_reset_error_is_not_called(self):
 
2019
        """on_password_reset_error is not called if incorrect app_name."""
 
2020
        self.patch(self.ui, 'on_password_reset_error', self._set_called)
 
2021
        mismatch_app_name = self.ui.app_name * 2
 
2022
        self.ui._signals['PasswordResetError'](mismatch_app_name, 'dummy')
 
2023
        self.assertFalse(self._called)
 
2024
 
 
2025
    def test_on_password_changed_is_not_called(self):
 
2026
        """on_password_changed is not called if incorrect app_name."""
 
2027
        self.patch(self.ui, 'on_password_changed', self._set_called)
 
2028
        mismatch_app_name = self.ui.app_name * 2
 
2029
        self.ui._signals['PasswordChanged'](mismatch_app_name, 'dummy')
 
2030
        self.assertFalse(self._called)
 
2031
 
 
2032
    def test_on_password_change_error_is_not_called(self):
 
2033
        """on_password_change_error is not called if incorrect app_name."""
 
2034
        self.patch(self.ui, 'on_password_change_error', self._set_called)
 
2035
        mismatch_app_name = self.ui.app_name * 2
 
2036
        self.ui._signals['PasswordChangeError'](mismatch_app_name, 'dummy')
 
2037
        self.assertFalse(self._called)
 
2038
 
 
2039
 
 
2040
class LoginOnlyTestCase(UbuntuSSOClientTestCase):
 
2041
    """Test suite for the login only GUI."""
 
2042
 
 
2043
    kwargs = dict(app_name=APP_NAME, tc_url=None, help_text=HELP_TEXT,
 
2044
                  login_only=True)
 
2045
 
 
2046
    def test_login_is_first_page(self):
 
2047
        """When starting, the login page is the first one."""
 
2048
        self.assert_pages_visibility(login=True)
 
2049
 
 
2050
    def test_no_back_button(self):
 
2051
        """There is no back button in the login screen."""
 
2052
        self.assertFalse(self.ui.login_back_button.get_property('visible'))
 
2053
 
 
2054
    def test_login_ok_button_has_the_focus(self):
 
2055
        """The login_ok_button has the focus."""
 
2056
        self.assertTrue(self.ui.login_ok_button.is_focus())
 
2057
 
 
2058
    def test_help_text_is_used(self):
 
2059
        """The passed help_text is used."""
 
2060
        self.assertEqual(self.ui.help_label.get_text(), HELP_TEXT)
 
2061
 
 
2062
 
 
2063
class SignalsTestCase(UbuntuSSOClientTestCase):
 
2064
    """Test the GTK signal emission."""
 
2065
 
 
2066
    def setUp(self):
 
2067
        """Init."""
 
2068
        super(SignalsTestCase, self).setUp()
 
2069
        self._called = {}
 
2070
        for sig_name, _ in gui.SIGNAL_ARGUMENTS:
 
2071
            self.ui.connect(sig_name, self._set_called, sig_name)
 
2072
 
 
2073
    def _set_called(self, widget, *args, **kwargs):
 
2074
        """Keep trace of signals emition."""
 
2075
        # pylint: disable=W0221
 
2076
        self._called[args[-1]] = (widget, args[:-1], kwargs)
 
2077
 
 
2078
    def test_closing_main_window_sends_outcome_as_signal(self):
 
2079
        """A signal is sent when closing the main window."""
 
2080
        self.ui.window.emit('delete-event', gtk.gdk.Event(gtk.gdk.DELETE))
 
2081
        expected = (self.ui.window, (APP_NAME,), {})
 
2082
        self.assertEqual(self._called[gui.SIG_USER_CANCELATION], expected)
 
2083
 
 
2084
    def test_every_cancel_emits_proper_signal(self):
 
2085
        """Clicking on any cancel button, 'user-cancelation' signal is sent."""
 
2086
        sig = gui.SIG_USER_CANCELATION
 
2087
        msg = 'user-cancelation is emitted when "%s" is clicked.'
 
2088
        buttons = filter(lambda name: 'cancel_button' in name, self.ui.widgets)
 
2089
        for name in buttons:
 
2090
            self.ui = self.gui_class(**self.kwargs)
 
2091
            self.ui.connect(sig, self._set_called, sig)
 
2092
            widget = getattr(self.ui, name)
 
2093
            widget.clicked()
 
2094
            expected_args = (self.ui.window, (APP_NAME,), {})
 
2095
            self.assertEqual(self._called[sig], expected_args, msg % name)
 
2096
            self._called = {}
 
2097
 
 
2098
    def test_on_user_registration_error_proper_signal_is_emitted(self):
 
2099
        """On UserRegistrationError, SIG_USER_CANCELATION signal is sent."""
 
2100
        self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
 
2101
        self.ui.on_close_clicked()
 
2102
        expected = (self.ui.window, (APP_NAME,), {})
 
2103
        self.assertEqual(expected,
 
2104
                         self._called[gui.SIG_USER_CANCELATION])
 
2105
 
 
2106
    def test_on_email_validated_proper_signals_is_emitted(self):
 
2107
        """On EmailValidated, 'registration-succeeded' signal is sent."""
 
2108
        self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
 
2109
        self.ui.on_close_clicked()
 
2110
        self.assertEqual((self.ui.window, (APP_NAME, EMAIL), {}),
 
2111
                          self._called[gui.SIG_REGISTRATION_SUCCEEDED])
 
2112
 
 
2113
    def test_on_email_validation_error_proper_signals_is_emitted(self):
 
2114
        """On EmailValidationError, SIG_USER_CANCELATION signal is sent."""
 
2115
        self.ui.on_email_validation_error(app_name=APP_NAME, error=self.error)
 
2116
        self.ui.on_close_clicked()
 
2117
        expected = (self.ui.window, (APP_NAME,), {})
 
2118
        self.assertEqual(expected,
 
2119
                         self._called[gui.SIG_USER_CANCELATION])
 
2120
 
 
2121
    def test_on_logged_in_proper_signals_is_emitted(self):
 
2122
        """On LoggedIn, 'login-succeeded' signal is sent."""
 
2123
        self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
 
2124
        self.ui.on_close_clicked()
 
2125
        self.assertEqual((self.ui.window, (APP_NAME, EMAIL), {}),
 
2126
                          self._called[gui.SIG_LOGIN_SUCCEEDED])
 
2127
 
 
2128
    def test_on_login_error_proper_signals_is_emitted(self):
 
2129
        """On LoginError, SIG_USER_CANCELATION signal is sent."""
 
2130
        self.click_connect_with_valid_data()
 
2131
        self.ui.on_login_error(app_name=APP_NAME, error=self.error)
 
2132
        self.ui.on_close_clicked()
 
2133
        expected = (self.ui.window, (APP_NAME,), {})
 
2134
        self.assertEqual(expected,
 
2135
                         self._called[gui.SIG_USER_CANCELATION])
 
2136
 
 
2137
    def test_registration_successfull_even_if_prior_registration_error(self):
 
2138
        """Only one signal is sent with the final outcome."""
 
2139
        self.click_join_with_valid_data()
 
2140
        self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
 
2141
        self.click_join_with_valid_data()
 
2142
        self.ui.on_email_validated(app_name=APP_NAME, email=EMAIL)
 
2143
        self.ui.on_close_clicked()
 
2144
 
 
2145
        self.assertEqual(len(self._called), 1)
 
2146
        self.assertTrue(gui.SIG_REGISTRATION_SUCCEEDED in self._called)
 
2147
 
 
2148
    def test_login_successfull_even_if_prior_login_error(self):
 
2149
        """Only one signal is sent with the final outcome."""
 
2150
        self.click_connect_with_valid_data()
 
2151
        self.ui.on_login_error(app_name=APP_NAME, error=self.error)
 
2152
        self.click_connect_with_valid_data()
 
2153
        self.ui.on_logged_in(app_name=APP_NAME, email=EMAIL)
 
2154
        self.ui.on_close_clicked()
 
2155
 
 
2156
        self.assertEqual(len(self._called), 1)
 
2157
        self.assertTrue(gui.SIG_LOGIN_SUCCEEDED in self._called)
 
2158
 
 
2159
    def test_user_cancelation_even_if_prior_registration_error(self):
 
2160
        """Only one signal is sent with the final outcome."""
 
2161
        self.click_join_with_valid_data()
 
2162
        self.ui.on_user_registration_error(app_name=APP_NAME, error=self.error)
 
2163
        self.ui.join_cancel_button.clicked()
 
2164
 
 
2165
        self.assertEqual(len(self._called), 1)
 
2166
        self.assertTrue(gui.SIG_USER_CANCELATION in self._called)
 
2167
 
 
2168
    def test_user_cancelation_even_if_prior_login_error(self):
 
2169
        """Only one signal is sent with the final outcome."""
 
2170
        self.click_connect_with_valid_data()
 
2171
        self.ui.on_login_error(app_name=APP_NAME, error=self.error)
 
2172
        self.ui.login_cancel_button.clicked()
 
2173
 
 
2174
        self.assertEqual(len(self._called), 1)
 
2175
        self.assertTrue(gui.SIG_USER_CANCELATION in self._called)
 
2176
 
 
2177
 
 
2178
class DefaultButtonsTestCase(UbuntuSSOClientTestCase):
 
2179
    """Each UI page has a default button when visible."""
 
2180
 
 
2181
    def setUp(self):
 
2182
        """Init."""
 
2183
        super(DefaultButtonsTestCase, self).setUp()
 
2184
        self.mapping = (
 
2185
            ('enter_details_vbox', 'join_ok_button'),
 
2186
            ('tc_browser_vbox', 'tc_back_button'),
 
2187
            ('verify_email_vbox', 'verify_token_button'),
 
2188
            ('login_vbox', 'login_ok_button'),
 
2189
            ('request_password_token_vbox',
 
2190
             'request_password_token_ok_button'),
 
2191
            ('set_new_password_vbox', 'set_new_password_ok_button'),
 
2192
            ('success_vbox', 'finish_close_button'),
 
2193
            ('error_vbox', 'finish_close_button'),
 
2194
            ('processing_vbox', None))
 
2195
 
 
2196
    def test_pages_have_default_widget_set(self):
 
2197
        """Each page has a proper button as default widget."""
 
2198
        msg = 'Page %r must have %r as default_widget (got %r instead).'
 
2199
        for pname, bname in self.mapping:
 
2200
            page = getattr(self.ui, pname)
 
2201
            button = bname and getattr(self.ui, bname)
 
2202
            self.assertTrue(page.default_widget is button,
 
2203
                            msg % (pname, bname, page.default_widget))
 
2204
 
 
2205
    def test_default_widget_can_default(self):
 
2206
        """Each default button can default."""
 
2207
        msg = 'Button %r must have can-default enabled.'
 
2208
        for _, bname in self.mapping:
 
2209
            if bname is not None:
 
2210
                button = getattr(self.ui, bname)
 
2211
                self.assertTrue(button.get_property('can-default'),
 
2212
                                msg % bname)
 
2213
 
 
2214
    def test_set_current_page_grabs_focus_for_default_button(self):
 
2215
        """Setting the current page marks the default widget as default."""
 
2216
        msg = '%r "has_default" must be True when %r if the current page.'
 
2217
        for pname, bname in self.mapping:
 
2218
            if bname is not None:
 
2219
                page = getattr(self.ui, pname)
 
2220
                self.patch(page.default_widget, 'grab_default',
 
2221
                           self._set_called)
 
2222
                self.ui._set_current_page(page)
 
2223
                self.assertEqual(self._called, ((), {}), msg % (bname, pname))
 
2224
                self._called = False