diff --git a/openslides/projector/api.py b/openslides/projector/api.py index a3d05bf9a..3bf27677e 100644 --- a/openslides/projector/api.py +++ b/openslides/projector/api.py @@ -67,6 +67,9 @@ def update_projector_overlay(overlay): def call_on_projector(calls): """ Sends data to the projector. + + The argument call has to be a dictionary with the javascript function name + as key and the argument for it as value. """ projector_js_cache = config['projector_js_cache'] projector_js_cache.update(calls) @@ -76,16 +79,21 @@ def call_on_projector(calls): def get_projector_content(slide_dict=None): """ - Returns the HTML-Content block of the projector. + Returns the HTML-Content block for the projector. + + Slide_dict has to be an dictonary with the key 'callback'. + + If slide_dict is None, use the active slide from the database. """ if slide_dict is None: slide_dict = config['projector_active_slide'].copy() callback = slide_dict.pop('callback', None) try: - return slide_callback[callback](**slide_dict) + slide_content = slide_callback[callback](**slide_dict) except KeyError: - return default_slide() + slide_content = default_slide() + return slide_content def default_slide(): @@ -120,7 +128,7 @@ def get_projector_overlays(): def get_projector_overlays_js(): """ - Returns JS-Code for the overlays. + Returns JS-Code for the active overlays. The retuned value is a list of json objects. """ diff --git a/openslides/projector/urls.py b/openslides/projector/urls.py index c1931c05b..2fc314762 100644 --- a/openslides/projector/urls.py +++ b/openslides/projector/urls.py @@ -17,16 +17,16 @@ from . import views urlpatterns = patterns( '', url(r'^$', - views.Projector.as_view(), + views.ProjectorView.as_view(), name='projector_show'), url(r'^preview/$', - views.Projector.as_view(), + views.ProjectorView.as_view(), {'callback': None}, name='projctor_preview_welcomepage'), url(r'^preview/(?P[^/]*)/$', - views.Projector.as_view(), + views.ProjectorView.as_view(), name='projector_preview'), url(r'^activate/(?P[^/]*)/$', diff --git a/openslides/projector/views.py b/openslides/projector/views.py index b9573011b..d1a8d0728 100644 --- a/openslides/projector/views.py +++ b/openslides/projector/views.py @@ -48,7 +48,7 @@ class DashboardView(AjaxMixin, TemplateView): return context -class Projector(TemplateView): +class ProjectorView(TemplateView): """ The Projector-Page. """ @@ -56,12 +56,9 @@ class Projector(TemplateView): template_name = 'projector.html' def get_context_data(self, **kwargs): - slide_dict = dict(self.request.GET.items()) callback = self.kwargs.get('callback', None) - if callback: - slide_dict['callback'] = callback - if not slide_dict: + if callback is None: kwargs.update({ 'content': get_projector_content(), 'overlays': get_projector_overlays(), @@ -70,11 +67,13 @@ class Projector(TemplateView): 'calls': config['projector_js_cache']}) # For the Preview else: + slide_dict = dict(self.request.GET.items()) + slide_dict['callback'] = callback kwargs.update({ - 'content': get_projector_content(slide_dict), + 'content': get_projector_content(slide_dict), 'reload': False}) - return super(Projector, self).get_context_data(**kwargs) + return super(ProjectorView, self).get_context_data(**kwargs) class ActivateView(RedirectView): @@ -86,10 +85,11 @@ class ActivateView(RedirectView): allow_ajax = True def pre_redirect(self, request, *args, **kwargs): - if kwargs['callback'] == 'mediafile' and \ - get_active_slide()['callback'] == 'mediafile': - # If the current slide is a pdf and the new page is also a slide, we dont have to use - # set_active_slide, because is causes a content reload. + if (kwargs['callback'] == 'mediafile' and + get_active_slide()['callback'] == 'mediafile'): + # If the current slide is a pdf and the new page is also a slide, + # we dont have to use set_active_slide, because is causes a content + # reload. kwargs.update({'page_num': 1, 'pk': request.GET.get('pk')}) url = Mediafile.objects.get(pk=kwargs['pk'], is_presentable=True).mediafile.url config['projector_active_slide'] = kwargs diff --git a/tests/projector/test_api.py b/tests/projector/test_api.py new file mode 100644 index 000000000..2c2acbaa0 --- /dev/null +++ b/tests/projector/test_api.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + Tests for openslides projector api + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + :copyright: 2011–2013 by OpenSlides team, see AUTHORS. + :license: GNU GPL, see LICENSE for more details. +""" + +from mock import MagicMock, patch + +from openslides.projector import api as projector_api +from openslides.utils.test import TestCase + + +class ApiFunctions(TestCase): + @patch('openslides.projector.api.get_projector_content') + @patch('openslides.projector.api.ProjectorSocketHandler') + def test_update_projector(self, mock_ProjectorSocketHandler, + mock_get_projector_content): + mock_get_projector_content.return_value = 'mock_string' + projector_api.update_projector() + mock_ProjectorSocketHandler.send_updates.assert_called_with( + {'content': 'mock_string'}) + + @patch('openslides.projector.api.get_overlays') + @patch('openslides.projector.api.ProjectorSocketHandler') + def test_update_projector_overlay(self, mock_ProjectorSocketHandler, + mock_get_overlays): + mock_overlay = MagicMock() + mock_overlay.name = 'mock_overlay_name' + mock_overlay.get_projector_html.return_value = 'mock_html_code' + mock_overlay.get_javascript.return_value = 'mock_javascript' + mock_get_overlays.return_value = {'mock_overlay': mock_overlay} + + # Test with active overlay + mock_overlay.is_active.return_value = False + projector_api.update_projector_overlay(None) + mock_ProjectorSocketHandler.send_updates.assert_called_with( + {'overlays': {'mock_overlay_name': None}}) + + # Test with active overlay + mock_overlay.is_active.return_value = True + projector_api.update_projector_overlay(None) + expected_data = {'overlays': {'mock_overlay_name': { + 'html': 'mock_html_code', + 'javascript': 'mock_javascript'}}} + mock_ProjectorSocketHandler.send_updates.assert_called_with(expected_data) + + # Test with overlay name as argument + projector_api.update_projector_overlay('mock_overlay') + mock_ProjectorSocketHandler.send_updates.assert_called_with(expected_data) + + # Test with overlay object as argument + projector_api.update_projector_overlay(mock_overlay) + mock_ProjectorSocketHandler.send_updates.assert_called_with(expected_data) + + @patch('openslides.projector.api.config') + @patch('openslides.projector.api.ProjectorSocketHandler') + def test_call_on_projector(self, mock_ProjectorSocketHandler, mock_config): + mock_config.__getitem__.return_value = {} + data = {'some_call': 'argument'} + projector_api.call_on_projector(data) + mock_ProjectorSocketHandler.send_updates.assert_called_with( + {'calls': data}) + mock_config.__getitem__.assert_called_with('projector_js_cache') + mock_config.__setitem__.assert_called_with('projector_js_cache', data) + + @patch('openslides.projector.api.default_slide') + def test_get_projector_content(self, mock_default_slide): + mock_slide = MagicMock() + mock_slide.return_value = 'slide content' + + with patch.dict('openslides.projector.api.slide_callback', + values={'mock_slide': mock_slide}): + value = projector_api.get_projector_content({'callback': 'mock_slide'}) + self.assertEqual(value, 'slide content') + + projector_api.get_projector_content({'callback': 'unknown_slide'}) + self.assertTrue(mock_default_slide.called) + + with patch('openslides.projector.api.config', + {'projector_active_slide': {'callback': 'mock_slide'}}): + value = projector_api.get_projector_content() + self.assertEqual(value, 'slide content') + + @patch('openslides.projector.api.render_to_string') + def test_default_slide(self, mock_render_to_string): + projector_api.default_slide() + mock_render_to_string.assert_called_with('projector/default_slide.html') + + @patch('openslides.projector.api.projector_overlays') + def test_get_overlays(self, mock_projector_overlays): + mock_overlay = MagicMock() + mock_overlay.name = 'mock_overlay' + mock_projector_overlays.send.return_value = ((None, mock_overlay), ) + + value = projector_api.get_overlays() + self.assertEqual(value, {'mock_overlay': mock_overlay}) + + @patch('openslides.projector.api.render_to_string') + @patch('openslides.projector.api.get_overlays') + def test_get_projector_overlays(self, mock_get_overlays, mock_render_to_string): + mock_overlay = MagicMock() + mock_overlay.get_projector_html.return_value = 'some html' + mock_get_overlays.return_value = {'overlay_name': mock_overlay} + + # Test with inactive overlay + mock_overlay.is_active.return_value = False + projector_api.get_projector_overlays() + mock_render_to_string.assert_called_with( + 'projector/all_overlays.html', + {'overlays': []}) + + # Test with active overlay + mock_overlay.is_active.return_value = True + projector_api.get_projector_overlays() + mock_render_to_string.assert_Called_with( + 'projector/all_overlays.html', + {'overlays': [{'name': 'overlay_name', 'html': 'some html'}]}) + + @patch('openslides.projector.api.get_overlays') + def test_get_projector_overlays_js(self, mock_get_overlays): + overlay = MagicMock() + mock_get_overlays.return_value = {'overlay': overlay} + + # Test with inactive overlay + overlay.is_active.return_value = False + value = projector_api.get_projector_overlays_js() + self.assertEqual(value, []) + + # Test with active overlay without js + overlay.is_active.return_value = True + overlay.get_javascript.return_value = None + value = projector_api.get_projector_overlays_js() + self.assertEqual(value, []) + + # Test with active overlay with js + overlay.get_javascript.return_value = 'some javascript' + value = projector_api.get_projector_overlays_js() + self.assertEqual(value, ['some javascript']) + + def test_register_slide(self): + mock_slide_callback = {} + with patch('openslides.projector.api.slide_callback', mock_slide_callback): + projector_api.register_slide('some name', 'some callback') + self.assertEqual(mock_slide_callback, {'some name': 'some callback'}) + + @patch('openslides.projector.api.render_to_string') + @patch('openslides.projector.api.register_slide') + def test_register_slide_model(self, mock_register_slide, mock_render_to_string): + mock_SlideModel = MagicMock() + mock_SlideModel.slide_callback_name = 'mock_callback_name' + mock_SlideModel.DoesNotExist = Exception + mock_slide_object = MagicMock() + mock_slide_object.get_slide_context.return_value = 'some context' + mock_SlideModel.objects.get.return_value = mock_slide_object + + projector_api.register_slide_model(mock_SlideModel, 'some template') + used_args, __ = mock_register_slide.call_args + self.assertEqual(used_args[0], 'mock_callback_name') + + # Test the generated slide function + used_args[1](pk=1) + mock_render_to_string.assert_called_with('some template', 'some context') + + # Test with non existing object + mock_SlideModel.objects.get.side_effect = Exception + used_args[1](pk=1) + mock_render_to_string.assert_called_with('some template', {'slide': None}) + + @patch('openslides.projector.api.update_projector_overlay') + @patch('openslides.projector.api.update_projector') + def test_set_active_slide(self, mock_update_projector, mock_update_projector_overlay): + mock_config = {} + with patch('openslides.projector.api.config', mock_config): + projector_api.set_active_slide('callback_name', {'some': 'kwargs'}) + self.assertEqual(mock_config, + {'projector_active_slide': {'callback': 'callback_name', + 'some': 'kwargs'}}) + mock_update_projector.assert_called_with() + mock_update_projector_overlay.assert_called_with(None) + + def test_get_active_slide(self): + mock_config = {'projector_active_slide': 'value'} + with patch('openslides.projector.api.config', mock_config): + value = projector_api.get_active_slide() + self.assertEqual(value, 'value') diff --git a/tests/projector/test_views.py b/tests/projector/test_views.py index e769667b0..733ed04b9 100644 --- a/tests/projector/test_views.py +++ b/tests/projector/test_views.py @@ -10,12 +10,62 @@ :license: GNU GPL, see LICENSE for more details. """ -from django.test.client import Client +from django.test.client import Client, RequestFactory +from mock import call, patch from openslides.projector.models import ProjectorSlide +from openslides.projector import views from openslides.utils.test import TestCase +class ProjectorViewTest(TestCase): + rf = RequestFactory() + + @patch('openslides.projector.views.get_projector_overlays_js') + @patch('openslides.projector.views.get_projector_overlays') + @patch('openslides.projector.views.get_projector_content') + def test_get(self, mock_get_projector_content, mock_get_projector_overlays, + mock_get_projector_overlays_js): + view = views.ProjectorView() + view.request = self.rf.get('/') + + # Test preview + view.kwargs = {'callback': 'slide_callback'} + context = view.get_context_data() + mock_get_projector_content.assert_called_with( + {'callback': 'slide_callback'}) + self.assertFalse(context['reload']) + + # Test live view + view.kwargs = {} + mock_config = {'projector_js_cache': 'js_cache'} + with patch('openslides.projector.views.config', mock_config): + context = view.get_context_data() + mock_get_projector_content.assert_called_with() + mock_get_projector_overlays.assert_called_with() + mock_get_projector_overlays_js.assert_called_with() + self.assertTrue(context['reload']) + self.assertEqual(context['calls'], 'js_cache') + + +class ActivateViewTest(TestCase): + rf = RequestFactory() + + @patch('openslides.projector.views.config') + @patch('openslides.projector.views.set_active_slide') + def test_get(self, mock_set_active_slide, mock_config): + view = views.ActivateView() + view.request = self.rf.get('/?some_key=some_value') + + view.pre_redirect(view.request, callback='some_callback') + + mock_set_active_slide.called_with('some_callback', + {'some_key': 'some_value'}) + mock_config.get_default.assert_has_calls([call('projector_scroll'), + call('projector_scale')]) + self.assertEqual(mock_config.__setitem__.call_count, 2) + + class CustomSlidesTest(TestCase): def setUp(self): self.admin_client = Client()