1
# -*- coding: utf-8 -*-
3
# test_gui - tests for ubuntu_sso.gui
5
# Author: Natalia Bidart <natalia.bidart@canonical.com>
7
# Copyright 2010 Canonical Ltd.
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.
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.
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."""
26
from collections import defaultdict
32
from twisted.trial.unittest import TestCase
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)
40
# Access to a protected member 'yyy' of a client class
41
# pylint: disable=W0212
43
# Instance of 'UbuntuSSOClientGUI' has no 'yyy' member
44
# pylint: disable=E1101,E1103
47
class FakedSSOBackend(object):
48
"""Fake a SSO Backend (acts as a dbus.Interface as well)."""
50
def __init__(self, *args, **kwargs):
54
for i in ('generate_captcha', 'login', 'register_user',
55
'validate_email', 'request_password_reset_token',
57
setattr(self, i, self._record_call(i))
59
def _record_call(self, func_name):
60
"""Store values when calling 'func_name'."""
62
def inner(*args, **kwargs):
63
"""Fake 'func_name'."""
64
self._called[func_name] = (args, kwargs)
69
class FakedDbusObject(object):
72
def __init__(self, *args, **kwargs):
78
class FakedSessionBus(object):
79
"""Fake the session bus."""
86
def add_signal_receiver(self, method, signal_name, dbus_interface):
87
"""Add a signal receiver."""
88
self.callbacks[(dbus_interface, signal_name)] = method
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)]
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)
107
class Settings(dict):
108
"""Faked embedded browser settings."""
110
def get_property(self, prop_name):
111
"""Alias for __getitem__."""
112
return self[prop_name]
114
def set_property(self, prop_name, newval):
115
"""Alias for __setitem__."""
116
self[prop_name] = newval
119
class FakedEmbeddedBrowser(gtk.TextView):
120
"""Faked an embedded browser."""
123
super(FakedEmbeddedBrowser, self).__init__()
125
self._signals = defaultdict(list)
126
self._settings = Settings()
128
def connect(self, signal_name, callback):
129
"""Connect 'signal_name' with 'callback'."""
130
self._signals[signal_name].append(callback)
132
def load_uri(self, uri):
133
"""Navigate to the given 'uri'."""
134
self._props['uri'] = uri
136
def set_property(self, prop_name, newval):
137
"""Set 'prop_name' to 'newval'."""
138
self._props[prop_name] = newval
140
def get_property(self, prop_name):
141
"""Return the current value for 'prop_name'."""
142
return self._props[prop_name]
144
def get_settings(self,):
145
"""Return the current settings."""
146
return self._settings
148
def get_load_status(self):
149
"""Return the current load status."""
150
return gui.WEBKIT_LOAD_FINISHED
153
"""Show this instance."""
154
self.set_property('visible', True)
157
class BasicTestCase(TestCase):
158
"""Test case with a helper tracker."""
162
self._called = False # helper
164
self.memento = MementoHandler()
165
self.memento.setLevel(logging.DEBUG)
166
gui.logger.addHandler(self.memento)
172
def _set_called(self, *args, **kwargs):
173
"""Set _called to True."""
174
self._called = (args, kwargs)
177
class LabeledEntryTestCase(BasicTestCase):
178
"""Test suite for the labeled entry."""
182
super(LabeledEntryTestCase, self).setUp()
183
self.label = 'Test me please'
184
self.entry = gui.LabeledEntry(label=self.label)
186
# we need a window to be able to realize ourselves
187
window = gtk.Window()
188
window.add(self.entry)
194
super(LabeledEntryTestCase, self).tearDown()
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)
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))
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))
217
def test_initial_text(self):
218
"""Entry have the correct text at startup."""
219
self.assert_correct_label()
221
def test_width_chars(self):
222
"""Entry have the correct width."""
223
self.assertEqual(self.entry.get_width_chars(), gui.DEFAULT_WIDTH)
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()
231
self.assertEqual(expected, actual, msg % (expected, actual))
233
def test_clear_entry_on_focus_in(self):
234
"""Entry are cleared when focused."""
237
msg = 'Entry must be cleared on focus in.'
238
self.assertEqual('', self.entry.get_text(), msg)
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)
246
self.assertEqual(((gtk.STATE_NORMAL, None), {}), self._called,
247
'Entry text color must be restore on focus in.')
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."""
252
self.grab_focus() # grab focus
253
self.grab_focus(focus_in=False) # loose focus
255
# Entry must be re-filled on focus out
256
self.assert_correct_label()
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."""
261
self.grab_focus() # grab focus
263
self.entry.set_text(' ') # add empty text to the entry
265
self.grab_focus(focus_in=False) # loose focus
267
# Entry must be re-filled on focus out
268
self.assert_correct_label()
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'
275
self.grab_focus() # grab focus
277
self.entry.set_text(expected) # add empty text to the entry
279
self.grab_focus(focus_in=False) # loose focus
281
self.assertEqual(expected, self.entry.get_text(), msg)
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'
288
self.grab_focus() # grab focus
290
self.entry.set_text(expected) # add text to the entry
292
self.grab_focus(focus_in=False) # loose focus
293
self.grab_focus() # grab focus again!
295
self.assertEqual(expected, self.entry.get_text(), msg)
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(), '')
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(), '')
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')
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'),
316
self.assertEqual(self.entry.get_property('secondary-icon-sensitive'),
318
self.assertEqual(self.entry.get_property('secondary-icon-activatable'),
320
prop = self.entry.get_property('secondary-icon-tooltip-text')
321
self.assertEqual(prop, None)
323
def test_set_warning(self):
324
"""Setting a warning show the proper secondary icon."""
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'),
332
self.assertEqual(self.entry.get_property('secondary-icon-activatable'),
334
prop = self.entry.get_property('secondary-icon-tooltip-text')
335
self.assertEqual(prop, msg)
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'),
343
self.assertEqual(self.entry.get_property('secondary-icon-sensitive'),
345
self.assertEqual(self.entry.get_property('secondary-icon-activatable'),
347
prop = self.entry.get_property('secondary-icon-tooltip-text')
348
self.assertEqual(prop, None)
351
class PasswordLabeledEntryTestCase(LabeledEntryTestCase):
352
"""Test suite for the labeled entry when is_password is True."""
356
super(PasswordLabeledEntryTestCase, self).setUp()
357
self.entry.is_password = True
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.')
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.')
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.')
378
class UbuntuSSOClientTestCase(BasicTestCase):
379
"""Basic setup and helper functions."""
381
gui_class = gui.UbuntuSSOClientGUI
382
kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT)
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',
392
self.ui = self.gui_class(**self.kwargs)
393
self.error = {'message': self.ui.UNKNOWN_ERROR}
397
self.ui.bus.callbacks = {}
399
super(UbuntuSSOClientTestCase, self).tearDown()
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)
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))
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, '')
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'))
427
# warning content is correct
428
actual = label.get_text()
429
self.assertEqual(actual, message)
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
436
def assert_correct_entry_warning(self, entry, message):
437
"""Check that a warning is shown displaying 'message'."""
438
self.assertEqual(entry.warning, message)
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)
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)
452
self.ui.name_entry.set_text(NAME)
454
self.ui.email1_entry.set_text(EMAIL)
455
self.ui.email2_entry.set_text(EMAIL)
457
self.ui.password1_entry.set_text(PASSWORD)
458
self.ui.password2_entry.set_text(PASSWORD)
460
self.ui.yes_to_tc_checkbutton.set_active(True)
461
# resolve captcha properly
462
self.ui.captcha_solution_entry.set_text(CAPTCHA_SOLUTION)
464
self.ui.join_ok_button.clicked()
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()
470
# resolve email token properly
471
self.ui.email_token_entry.set_text(EMAIL_TOKEN)
473
self.ui.verify_token_button.clicked()
475
def click_connect_with_valid_data(self):
476
"""Move to the next page after entering login info."""
478
self.ui.login_email_entry.set_text(EMAIL)
480
self.ui.login_password_entry.set_text(PASSWORD)
482
self.ui.login_ok_button.clicked()
484
def click_request_password_token_with_valid_data(self):
485
"""Move to the next page after requesting for password reset token."""
487
self.ui.reset_email_entry.set_text(EMAIL)
489
self.ui.request_password_token_ok_button.clicked()
491
def click_set_new_password_with_valid_data(self):
492
"""Move to the next page after resetting password."""
494
self.ui.reset_code_entry.set_text(RESET_PASSWORD_TOKEN)
496
self.ui.reset_password1_entry.set_text(PASSWORD)
497
self.ui.reset_password2_entry.set_text(PASSWORD)
499
self.ui.set_new_password_ok_button.clicked()
502
class BasicUbuntuSSOClientTestCase(UbuntuSSOClientTestCase):
503
"""Test suite for basic functionality."""
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'))
509
def test_main_window_is_resizable(self):
510
"""The main window can be resized."""
511
self.assertTrue(self.ui.window.get_property('resizable'))
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.')
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)
524
def test_session_bus_is_correct(self):
525
"""The session bus is created and is correct."""
526
self.assertIsInstance(self.ui.bus, FakedSessionBus)
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)
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)
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)
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
554
self.ui.on_close_clicked()
556
self.assertEqual(self.ui.bus.callbacks, {})
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.')
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
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)
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())
584
# text content is correct
585
self.assertEqual(expected, actual, msg % (name, expected, actual))
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,))
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]
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),
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,
609
for name in passwords:
610
widget = getattr(self.ui, name)
611
self.assertTrue(widget.is_password, msg % name)
613
def test_warning_fields_are_cleared(self):
614
"""Every warning label should be cleared."""
615
self.assert_warnings_visibility()
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)
623
self.ui = self.gui_class(close_callback=self._set_called,
625
widget = getattr(self.ui, name)
627
self.assertEqual(self._called, ((widget,), {}), msg % name)
630
def test_window_icon(self):
631
"""Main window has the proper icon."""
632
self.assertEqual('ubuntu-logo', self.ui.window.get_icon_name())
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.')
640
def test_transient_window_is_correct(self):
641
"""The transient window is correct."""
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,), {}))
649
def test_transient_window_accepts_negative_id(self):
650
"""The transient window accepts a negative window id."""
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,), {}))
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())
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())
669
class EnterDetailsTestCase(UbuntuSSOClientTestCase):
670
"""Test suite for the user registration (enter details page)."""
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))
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)
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',
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',
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))
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)
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)
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()
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,
726
dict(reply_handler=gui.NO_OP,
727
error_handler=gui.NO_OP)))
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)
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()
738
self.assertTrue(self.ui.processing_vbox.get_property('visible'),
739
'the processing box should be visible.')
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.')
745
spinner, label = box.get_children()
746
self.assertIsInstance(spinner, gtk.Spinner)
747
self.assertIsInstance(label, gtk.Label)
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.')
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.')
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)
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()
774
self.assertFalse(os.path.exists(self.ui._captcha_filename),
775
'captcha image must be removed when exiting.')
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.')
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.')
786
spinner, label = box.get_children()
787
self.assertIsInstance(spinner, gtk.Spinner)
788
self.assertIsInstance(label, gtk.Label)
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.')
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())
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())
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.')
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)
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)))
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,), {}))
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'))
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")
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, ((), {}))
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)
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)
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)
869
self.ui.join_ok_button.set_sensitive(False)
870
self.ui.join_ok_button.clicked()
871
self.assertFalse(self._called)
873
self.ui.join_ok_button.set_sensitive(True)
874
self.ui.join_ok_button.clicked()
875
self.assertTrue(self._called)
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)
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, ((), {}))
892
class NoTermsAndConditionsTestCase(UbuntuSSOClientTestCase):
893
"""Test suite for the user registration (with no t&c link)."""
895
kwargs = dict(app_name=APP_NAME, tc_url='', help_text=HELP_TEXT)
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)
902
class TermsAndConditionsTestCase(UbuntuSSOClientTestCase):
903
"""Test suite for the user registration (terms & conditions page)."""
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)
911
class TermsAndConditionsBrowserTestCase(UbuntuSSOClientTestCase):
912
"""Test suite for the terms & conditions browser."""
915
super(TermsAndConditionsBrowserTestCase, self).setUp()
916
self.patch(webkit, 'WebView', FakedEmbeddedBrowser)
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]
924
self.ui.tc_browser_vbox.hide()
925
super(TermsAndConditionsBrowserTestCase, self).tearDown()
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)
931
children = self.ui.tc_browser_window.get_children()
932
self.assertEqual(1, len(children))
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'))
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'))
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, ((), {}))
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)
956
self.ui.tc_browser_vbox.hide()
958
children = self.ui.tc_browser_window.get_children()
959
self.assertEqual(0, len(children))
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)
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)
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)
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())
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)
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'],
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.'
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'],
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)
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)
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)
1017
decision = webkit.WebPolicyDecision()
1018
decision.use = self._set_called
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, ((), {}))
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)
1030
decision = webkit.WebPolicyDecision()
1031
decision.ignore = self._set_called
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, ((), {}))
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)
1044
def test_navigation_requested_opens_links_when_clicked(self):
1045
"""The navigation request is opened on user's default browser
1047
(If the user opened a link by clicking into it).
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)
1055
decision = webkit.WebPolicyDecision()
1056
decision.ignore = gui.NO_OP
1058
self.patch(gui.webbrowser, 'open', self._set_called)
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,), {}))
1066
class RegistrationErrorTestCase(UbuntuSSOClientTestCase):
1067
"""Test suite for the user registration error handling."""
1071
super(RegistrationErrorTestCase, self).setUp()
1072
self.click_join_with_valid_data()
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)
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, ((), {}))
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)
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.'}
1099
self.ui.on_user_registration_error(app_name=APP_NAME, error=error)
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,
1105
self.assert_correct_entry_warning(self.ui.email2_entry,
1107
self.assert_correct_entry_warning(self.ui.password1_entry,
1109
self.assert_correct_entry_warning(self.ui.password2_entry,
1113
class VerifyEmailTestCase(UbuntuSSOClientTestCase):
1114
"""Test suite for the user registration (verify email page)."""
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()
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)
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,
1133
self.assertEqual(expected, actual, msg % (expected, actual))
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)))
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)))
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)
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()
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)
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())
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)
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.'}
1192
self.ui.on_email_validation_error(app_name=APP_NAME, error=error)
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'])
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)
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)
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'))
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)
1222
self.ui.verify_token_button.set_sensitive(False)
1223
self.ui.verify_token_button.clicked()
1224
self.assertFalse(self._called)
1226
self.ui.verify_token_button.set_sensitive(True)
1227
self.ui.verify_token_button.clicked()
1228
self.assertTrue(self._called)
1231
class VerifyEmailValidationTestCase(UbuntuSSOClientTestCase):
1232
"""Test suite for the user registration validation (verify email page)."""
1236
super(VerifyEmailValidationTestCase, self).setUp()
1237
self.ui.on_user_registered(app_name=APP_NAME, email=EMAIL)
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('')
1243
self.ui.verify_token_button.clicked()
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)
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()
1254
self.assert_warnings_visibility()
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()
1261
# this will certainly NOT generate warnings
1262
self.click_verify_email_with_valid_data()
1264
self.assert_warnings_visibility()
1267
class VerifyEmailLoginOnlyTestCase(VerifyEmailTestCase):
1268
"""Test suite for the user login (verify email page)."""
1270
kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT,
1274
class VerifyEmailValidationLoginOnlyTestCase(VerifyEmailValidationTestCase):
1275
"""Test suite for the user login validation (verify email page)."""
1277
kwargs = dict(app_name=APP_NAME, tc_url=TC_URL, help_text=HELP_TEXT,
1281
class RegistrationValidationTestCase(UbuntuSSOClientTestCase):
1282
"""Test suite for the user registration validations."""
1286
super(RegistrationValidationTestCase, self).setUp()
1287
self.ui.join_ok_button.set_sensitive(True)
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('')
1293
self.ui.join_ok_button.clicked()
1295
self.assert_correct_entry_warning(self.ui.name_entry,
1296
self.ui.FIELD_REQUIRED)
1297
self.assertNotIn('register_user', self.ui.backend._called)
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).'
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('')
1309
self.ui.join_ok_button.clicked()
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)
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)
1322
self.ui.join_ok_button.clicked()
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)
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')
1335
self.ui.join_ok_button.clicked()
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)
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)
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)
1356
self.ui.join_ok_button.clicked()
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)
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)
1371
self.ui.join_ok_button.clicked()
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)
1379
def test_warning_is_shown_if_tc_not_accepted(self):
1380
"""A warning message is shown if TC are not accepted."""
1382
self.ui.yes_to_tc_checkbutton.set_active(False)
1384
self.ui.join_ok_button.clicked()
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)
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('')
1395
self.ui.join_ok_button.clicked()
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)
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()
1406
self.assert_warnings_visibility()
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()
1413
# this will certainly NOT generate warnings
1414
self.click_join_with_valid_data()
1416
self.assert_warnings_visibility()
1419
class LoginTestCase(UbuntuSSOClientTestCase):
1420
"""Test suite for the user login pages."""
1424
super(LoginTestCase, self).setUp()
1425
self.ui.login_button.clicked()
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)
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))
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))
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)
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',
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)
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)
1467
def test_on_login_connect_button_clicked(self):
1468
"""Clicking login_ok_button calls backend.login."""
1469
self.click_connect_with_valid_data()
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)))
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)
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)
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)
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)
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)
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.'}
1514
self.ui.on_login_error(app_name=APP_NAME, error=error)
1516
expected = '\n'.join((error['__all__'], error['message']))
1517
self.assert_correct_label_warning(self.ui.warning_label, expected)
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()
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)
1530
self.ui.login_ok_button.set_sensitive(False)
1531
self.ui.login_ok_button.clicked()
1532
self.assertFalse(self._called)
1534
self.ui.login_ok_button.set_sensitive(True)
1535
self.ui.login_ok_button.clicked()
1536
self.assertTrue(self._called)
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)
1545
class LoginValidationTestCase(UbuntuSSOClientTestCase):
1546
"""Test suite for the user login validation."""
1550
super(LoginValidationTestCase, self).setUp()
1551
self.ui.login_button.clicked()
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('')
1557
self.ui.login_ok_button.clicked()
1559
self.assert_correct_entry_warning(self.ui.login_email_entry,
1560
self.ui.FIELD_REQUIRED)
1561
self.assertNotIn('login', self.ui.backend._called)
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')
1567
self.ui.login_ok_button.clicked()
1569
self.assert_correct_entry_warning(self.ui.login_email_entry,
1570
self.ui.EMAIL_INVALID)
1571
self.assertNotIn('login', self.ui.backend._called)
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('')
1577
self.ui.login_ok_button.clicked()
1579
self.assert_correct_entry_warning(self.ui.login_password_entry,
1580
self.ui.FIELD_REQUIRED)
1581
self.assertNotIn('login', self.ui.backend._called)
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()
1588
self.assert_warnings_visibility()
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()
1595
# this will certainly NOT generate warnings
1596
self.click_connect_with_valid_data()
1598
self.assert_warnings_visibility()
1601
class ResetPasswordTestCase(UbuntuSSOClientTestCase):
1602
"""Test suite for the reset password functionality."""
1606
super(ResetPasswordTestCase, self).setUp()
1607
self.ui.login_button.clicked()
1608
self.ui.forgotten_password_button.clicked()
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)
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)
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)
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(),
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)
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)
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())
1644
self.ui.reset_email_entry.set_text('a')
1645
self.assertTrue(is_sensitive())
1647
self.ui.reset_email_entry.set_text('')
1648
self.assertFalse(is_sensitive())
1650
self.ui.reset_email_entry.set_text(' ')
1651
self.assertFalse(is_sensitive())
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)
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],
1665
dict(reply_handler=gui.NO_OP,
1666
error_handler=gui.NO_OP)))
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)
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)
1679
self.assertEqual(self.ui.help_label.get_text(),
1680
self.ui.SET_NEW_PASSWORD_LABEL % {'email': EMAIL})
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)
1687
self.assertEqual(self.ui.set_new_password_ok_button.get_label(),
1688
self.ui.RESET_PASSWORD)
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)
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.'}
1703
self.ui.on_password_reset_error(app_name=APP_NAME, error=error)
1705
expected = '\n'.join((error['__all__'], error['message']))
1706
self.assert_correct_label_warning(self.ui.warning_label, expected)
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)
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)
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)
1721
class ResetPasswordValidationTestCase(UbuntuSSOClientTestCase):
1722
"""Test suite for the password reset validations."""
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(' ')
1728
self.ui.request_password_token_ok_button.set_sensitive(True)
1729
self.ui.request_password_token_ok_button.clicked()
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)
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')
1740
self.ui.request_password_token_ok_button.clicked()
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)
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()
1752
self.assert_warnings_visibility()
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()
1759
# this will certainly NOT generate warnings
1760
self.click_request_password_token_with_valid_data()
1762
self.assert_warnings_visibility()
1765
class SetNewPasswordTestCase(UbuntuSSOClientTestCase):
1766
"""Test suite for setting a new password functionality."""
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)
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())
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):
1785
for entry, val in zip(entries, values):
1787
expected &= bool(val and not val.isspace())
1789
actual = self.ui.set_new_password_ok_button.get_sensitive()
1790
self.assertEqual(expected, actual, msg % (expected, values))
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)
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)))
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)
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)
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.'}
1827
self.ui.on_password_change_error(app_name=APP_NAME, error=error)
1829
expected = '\n'.join((error['__all__'], error['message']))
1830
self.assert_correct_label_warning(self.ui.warning_label, expected)
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)
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)
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)
1845
class SetNewPasswordValidationTestCase(UbuntuSSOClientTestCase):
1846
"""Test suite for validations for setting a new password."""
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('')
1852
self.ui.set_new_password_ok_button.set_sensitive(True)
1853
self.ui.set_new_password_ok_button.clicked()
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)
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)
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)
1872
self.ui.set_new_password_ok_button.set_sensitive(True)
1873
self.ui.set_new_password_ok_button.clicked()
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)
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)
1888
self.ui.set_new_password_ok_button.set_sensitive(True)
1889
self.ui.set_new_password_ok_button.clicked()
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)
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()
1902
self.assert_warnings_visibility()
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()
1909
# this will certainly NOT generate warnings
1910
self.click_set_new_password_with_valid_data()
1912
self.assert_warnings_visibility()
1915
class DbusTestCase(UbuntuSSOClientTestCase):
1916
"""Test suite for the dbus calls."""
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)
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))
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 = []
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
2040
class LoginOnlyTestCase(UbuntuSSOClientTestCase):
2041
"""Test suite for the login only GUI."""
2043
kwargs = dict(app_name=APP_NAME, tc_url=None, help_text=HELP_TEXT,
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)
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'))
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())
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)
2063
class SignalsTestCase(UbuntuSSOClientTestCase):
2064
"""Test the GTK signal emission."""
2068
super(SignalsTestCase, self).setUp()
2070
for sig_name, _ in gui.SIGNAL_ARGUMENTS:
2071
self.ui.connect(sig_name, self._set_called, sig_name)
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)
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)
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)
2094
expected_args = (self.ui.window, (APP_NAME,), {})
2095
self.assertEqual(self._called[sig], expected_args, msg % name)
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])
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])
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])
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])
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])
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()
2145
self.assertEqual(len(self._called), 1)
2146
self.assertTrue(gui.SIG_REGISTRATION_SUCCEEDED in self._called)
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()
2156
self.assertEqual(len(self._called), 1)
2157
self.assertTrue(gui.SIG_LOGIN_SUCCEEDED in self._called)
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()
2165
self.assertEqual(len(self._called), 1)
2166
self.assertTrue(gui.SIG_USER_CANCELATION in self._called)
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()
2174
self.assertEqual(len(self._called), 1)
2175
self.assertTrue(gui.SIG_USER_CANCELATION in self._called)
2178
class DefaultButtonsTestCase(UbuntuSSOClientTestCase):
2179
"""Each UI page has a default button when visible."""
2183
super(DefaultButtonsTestCase, self).setUp()
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))
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))
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'),
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',
2222
self.ui._set_current_page(page)
2223
self.assertEqual(self._called, ((), {}), msg % (bname, pname))
2224
self._called = False