from __future__ import absolute_import from __future__ import print_function import datetime import os import ujson from django.http import HttpResponse from mock import MagicMock, patch from six.moves import urllib from typing import Any, Dict from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_helpers import HostRequestMock from zerver.lib.test_runner import slow from zerver.models import get_realm, get_stream, get_user from zerver.views.home import home, sent_time_in_epoch_seconds class HomeTest(ZulipTestCase): @slow('big method') def test_home(self): # type: () -> None # Keep this list sorted!!! html_bits = [ 'Compose your message here...', 'Exclude messages with topic', 'Get started', 'Keyboard shortcuts', 'Loading...', 'Manage streams', 'Narrow by topic', 'Next message', 'SHARE THE LOVE', 'Search streams', 'Welcome to Zulip', 'pygments.css', 'var page_params', ] # Keep this list sorted!!! expected_keys = [ "alert_words", "attachments", "autoscroll_forever", "avatar_source", "avatar_url", "avatar_url_medium", "can_create_streams", "cross_realm_bots", "custom_profile_fields", "debug_mode", "default_desktop_notifications", "default_language", "default_language_name", "development_environment", "email", "emoji_alt_code", "emojiset", "emojiset_choices", "enable_desktop_notifications", "enable_digest_emails", "enable_offline_email_notifications", "enable_offline_push_notifications", "enable_online_push_notifications", "enable_sounds", "enable_stream_desktop_notifications", "enable_stream_sounds", "enter_sends", "first_in_realm", "full_name", "furthest_read_time", "has_mobile_devices", "have_initial_messages", "hotspots", "initial_servertime", "is_admin", "language_list", "language_list_dbl_col", "last_event_id", "left_side_userlist", "login_page", "max_avatar_file_size", "max_icon_file_size", "max_message_id", "maxfilesize", "muted_topics", "narrow", "narrow_stream", "needs_tutorial", "never_subscribed", "pm_content_in_desktop_notifications", "pointer", "poll_timeout", "presences", "prompt_for_invites", "queue_id", "realm_add_emoji_by_admins_only", "realm_allow_message_editing", "realm_authentication_methods", "realm_bot_domain", "realm_bots", "realm_create_stream_by_admins_only", "realm_default_language", "realm_default_streams", "realm_description", "realm_domains", "realm_email_changes_disabled", "realm_emoji", "realm_filters", "realm_icon_source", "realm_icon_url", "realm_inline_image_preview", "realm_inline_url_embed_preview", "realm_invite_by_admins_only", "realm_invite_required", "realm_is_zephyr_mirror_realm", "realm_mandatory_topics", "realm_message_content_edit_limit_seconds", "realm_message_retention_days", "realm_name", "realm_name_changes_disabled", "realm_notifications_stream_id", "realm_password_auth_enabled", "realm_presence_disabled", "realm_restricted_to_domain", "realm_show_digest_email", "realm_uri", "realm_users", "realm_waiting_period_threshold", "referrals", "save_stacktraces", "server_generation", "server_inline_image_preview", "server_inline_url_embed_preview", "server_uri", "share_the_love", "subscriptions", "test_suite", "timezone", "twenty_four_hour_time", "unread_count", "unsubscribed", "use_websockets", "user_id", "zulip_version", ] email = self.example_email("hamlet") # Verify fails if logged-out result = self.client_get('/') self.assertEqual(result.status_code, 302) self.login(email) # Create bot for realm_bots testing. Must be done before fetching home_page. bot_info = { 'full_name': 'The Bot of Hamlet', 'short_name': 'hambot', } self.client_post("/json/bots", bot_info) # Verify succeeds once logged-in result = self._get_home_page(stream='Denmark') html = result.content.decode('utf-8') for html_bit in html_bits: if html_bit not in html: raise AssertionError('%s not in result' % (html_bit,)) page_params = self._get_page_params(result) actual_keys = sorted([str(k) for k in page_params.keys()]) self.assertEqual(actual_keys, expected_keys) # TODO: Inspect the page_params data further. # print(ujson.dumps(page_params, indent=2)) realm_bots_expected_keys = [ 'api_key', 'avatar_url', 'bot_type', 'default_all_public_streams', 'default_events_register_stream', 'default_sending_stream', 'email', 'full_name', 'is_active', 'owner', 'user_id', ] realm_bots_actual_keys = sorted([str(key) for key in page_params['realm_bots'][0].keys()]) self.assertEqual(realm_bots_actual_keys, realm_bots_expected_keys) def _get_home_page(self, **kwargs): # type: (**Any) -> HttpResponse with \ patch('zerver.lib.events.request_event_queue', return_value=42), \ patch('zerver.lib.events.get_user_events', return_value=[]): result = self.client_get('/', dict(**kwargs)) return result def _get_page_params(self, result): # type: (HttpResponse) -> Dict[str, Any] html = result.content.decode('utf-8') lines = html.split('\n') page_params_line = [l for l in lines if l.startswith('var page_params')][0] page_params_json = page_params_line.split(' = ')[1].rstrip(';') page_params = ujson.loads(page_params_json) return page_params def _sanity_check(self, result): # type: (HttpResponse) -> None ''' Use this for tests that are geared toward specific edge cases, but which still want the home page to load properly. ''' html = result.content.decode('utf-8') if 'Compose your message' not in html: raise AssertionError('Home page probably did not load.') def test_terms_of_service(self): # type: () -> None user = self.example_user('hamlet') email = user.email self.login(email) for user_tos_version in [None, '1.1', '2.0.3.4']: user.tos_version = user_tos_version user.save() with \ self.settings(TERMS_OF_SERVICE='whatever'), \ self.settings(TOS_VERSION='99.99'): result = self.client_get('/', dict(stream='Denmark')) html = result.content.decode('utf-8') self.assertIn('There are new Terms of Service', html) def test_terms_of_service_first_time_template(self): # type: () -> None user = self.example_user('hamlet') email = user.email self.login(email) user.tos_version = None user.save() with \ self.settings(FIRST_TIME_TOS_TEMPLATE='hello.html'), \ self.settings(TOS_VERSION='99.99'): result = self.client_post('/accounts/accept_terms/') self.assertEqual(result.status_code, 200) self.assert_in_response("I agree to the", result) self.assert_in_response("most productive group chat", result) def test_accept_terms_of_service(self): # type: () -> None email = self.example_email("hamlet") self.login(email) result = self.client_post('/accounts/accept_terms/') self.assertEqual(result.status_code, 200) self.assert_in_response("I agree to the", result) result = self.client_post('/accounts/accept_terms/', {'terms': True}) self.assertEqual(result.status_code, 302) self.assertEqual(result['Location'], '/') def test_bad_narrow(self): # type: () -> None email = self.example_email("hamlet") self.login(email) with patch('logging.exception') as mock: result = self._get_home_page(stream='Invalid Stream') mock.assert_called_once_with('Narrow parsing') self._sanity_check(result) def test_bad_pointer(self): # type: () -> None user_profile = self.example_user('hamlet') email = user_profile.email user_profile.pointer = 999999 user_profile.save() self.login(email) with patch('logging.warning') as mock: result = self._get_home_page() mock.assert_called_once_with('hamlet@zulip.com has invalid pointer 999999') self._sanity_check(result) def test_topic_narrow(self): # type: () -> None email = self.example_email("hamlet") self.login(email) result = self._get_home_page(stream='Denmark', topic='lunch') self._sanity_check(result) html = result.content.decode('utf-8') self.assertIn('lunch', html) def test_notifications_stream(self): # type: () -> None email = self.example_email("hamlet") realm = get_realm('zulip') realm.notifications_stream = get_stream('Denmark', realm) realm.save() self.login(email) result = self._get_home_page() page_params = self._get_page_params(result) self.assertEqual(page_params['realm_notifications_stream_id'], get_stream('Denmark', realm).id) def test_people(self): # type: () -> None email = self.example_email('hamlet') realm = get_realm('zulip') self.login(email) result = self._get_home_page() page_params = self._get_page_params(result) for params in ['realm_users', 'realm_bots']: users = page_params['realm_users'] self.assertTrue(len(users) >= 3) for user in users: self.assertEqual(user['user_id'], get_user(user['email'], realm).id) cross_bots = page_params['cross_realm_bots'] self.assertEqual(len(cross_bots), 3) cross_bots.sort(key=lambda d: d['email']) notification_bot = self.notification_bot() self.assertEqual(cross_bots, [ dict( user_id=get_user('feedback@zulip.com', get_realm('zulip')).id, is_admin=False, email='feedback@zulip.com', full_name='Zulip Feedback Bot', is_bot=True ), dict( user_id=notification_bot.id, is_admin=False, email=notification_bot.email, full_name='Notification Bot', is_bot=True ), dict( user_id=get_user('welcome-bot@zulip.com', get_realm('zulip')).id, is_admin=False, email='welcome-bot@zulip.com', full_name='Welcome Bot', is_bot=True ), ]) def test_new_stream(self): # type: () -> None email = self.example_email("hamlet") stream_name = 'New stream' self.subscribe_to_stream(email, stream_name) self.login(email) result = self._get_home_page(stream=stream_name) page_params = self._get_page_params(result) self.assertEqual(page_params['narrow_stream'], stream_name) self.assertEqual(page_params['narrow'], [dict(operator='stream', operand=stream_name)]) self.assertEqual(page_params['pointer'], -1) self.assertEqual(page_params['max_message_id'], -1) self.assertEqual(page_params['have_initial_messages'], False) def test_invites_by_admins_only(self): # type: () -> None user_profile = self.example_user('hamlet') email = user_profile.email realm = user_profile.realm realm.invite_by_admins_only = True realm.save() self.login(email) self.assertFalse(user_profile.is_realm_admin) result = self._get_home_page() html = result.content.decode('utf-8') self.assertNotIn('Invite more users', html) user_profile.is_realm_admin = True user_profile.save() result = self._get_home_page() html = result.content.decode('utf-8') self.assertIn('Invite more users', html) def test_desktop_home(self): # type: () -> None email = self.example_email("hamlet") self.login(email) result = self.client_get("/desktop_home") self.assertEqual(result.status_code, 301) self.assertTrue(result["Location"].endswith("/desktop_home/")) result = self.client_get("/desktop_home/") self.assertEqual(result.status_code, 302) path = urllib.parse.urlparse(result['Location']).path self.assertEqual(path, "/") def test_apps_view(self): # type: () -> None result = self.client_get('/apps') self.assertEqual(result.status_code, 301) self.assertTrue(result['Location'].endswith('/apps/')) with self.settings(ZILENCER_ENABLED=False): result = self.client_get('/apps/') self.assertEqual(result.status_code, 301) self.assertTrue(result['Location'] == 'https://zulipchat.com/apps/') with self.settings(ZILENCER_ENABLED=True): result = self.client_get('/apps/') self.assertEqual(result.status_code, 200) html = result.content.decode('utf-8') self.assertIn('Appsolutely', html) def test_generate_204(self): # type: () -> None email = self.example_email("hamlet") self.login(email) result = self.client_get("/api/v1/generate_204") self.assertEqual(result.status_code, 204) def test_message_sent_time(self): # type: () -> None epoch_seconds = 1490472096 pub_date = datetime.datetime.fromtimestamp(epoch_seconds) user_message = MagicMock() user_message.message.pub_date = pub_date self.assertEqual(sent_time_in_epoch_seconds(user_message), epoch_seconds) def test_handlebars_compile_error(self): # type: () -> None request = HostRequestMock() with self.settings(DEVELOPMENT=True): with patch('os.path.exists', return_value=True): result = home(request) self.assertEqual(result.status_code, 500) self.assert_in_response('Error compiling handlebars templates.', result) def test_subdomain_homepage(self): # type: () -> None email = self.example_email("hamlet") self.login(email) with self.settings(SUBDOMAINS_HOMEPAGE=True): with patch('zerver.views.home.get_subdomain', return_value=""): result = self._get_home_page() self.assertEqual(result.status_code, 200) self.assert_in_response('most productive group chat', result) with patch('zerver.views.home.get_subdomain', return_value="subdomain"): result = self._get_home_page() self._sanity_check(result)