diff --git a/dav_base/tests/generic.py b/dav_base/tests/generic.py index c4c2c27..54498b8 100644 --- a/dav_base/tests/generic.py +++ b/dav_base/tests/generic.py @@ -3,6 +3,7 @@ import os from django.apps import apps from django.contrib.auth.models import AbstractUser from django.contrib.staticfiles.testing import StaticLiveServerTestCase +from django.core import mail as django_mail from django.test import SimpleTestCase, TestCase, tag from django.urls import reverse from selenium import webdriver @@ -44,6 +45,15 @@ class EmailTestMixin(object): email_base_url = 'http://localhost' email_subject_prefix = '[Test]' + def get_mail_for_user(self, user): + recipient = '"{fullname}" <{email}>'.format(fullname=user.get_full_name(), email=user.email) + mails = [] + for mail in django_mail.outbox: + if recipient in mail.recipients(): + mails.append(mail) + + return mails + def assertSender(self, mail): self.assertEqual(mail.from_email, self.email_sender) diff --git a/dav_base/tests/test_templates.py b/dav_base/tests/test_templates.py index fa0cb5a..cacf9ba 100644 --- a/dav_base/tests/test_templates.py +++ b/dav_base/tests/test_templates.py @@ -35,9 +35,10 @@ class TemplatesTestCase(SimpleTestCase): '', ) + content = response.content.decode('utf-8') for needle in html_needles: needle = needle.format(static_url=static_url) - self.assertInHTML(needle, response.content.decode('utf-8')) + self.assertInHTML(needle, content) def test_page_footer(self): response = self.response_from_root_view diff --git a/dav_events/tests/generic.py b/dav_events/tests/generic.py index 43b6834..670ff44 100644 --- a/dav_events/tests/generic.py +++ b/dav_events/tests/generic.py @@ -1,7 +1,12 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals from django.apps import apps from django.contrib.auth import get_user_model from django.contrib.auth.models import Group +from ..models.event import Event +from ..models.eventstatus import EventStatus + class RoleMixin(object): def create_user_for_role(self, role_name, password, first_name, last_name): @@ -20,3 +25,21 @@ class RoleMixin(object): setattr(app_config.settings, setting_name, [role_name]) return user + + +class EventMixin(object): + def get_status_label(self, status_code): + return EventStatus.objects.get(code=status_code).label + + def create_event(self, data): + event = Event(**data) + event.save() + return event + + def submit_event(self, event): + event.workflow.update_status('submitted', event.owner) + return event + + def accept_event(self, event, user): + event.workflow.update_status('accepted', user) + return event diff --git a/dav_events/tests/test_emails.py b/dav_events/tests/test_emails.py index d161ef5..e48716e 100644 --- a/dav_events/tests/test_emails.py +++ b/dav_events/tests/test_emails.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from __future__ import unicode_literals import datetime from django.apps import apps from django.contrib.auth import get_user_model @@ -7,25 +8,24 @@ from django.test import TestCase from dav_base.tests.generic import EmailTestMixin -from ..models.event import Event from ..models.oneclickaction import OneClickAction -from .generic import RoleMixin +from .generic import RoleMixin, EventMixin TEST_EVENT_DATA = { - 'title': u'Täst', - 'description': u'Teßt', + 'title': 'Täst', + 'description': 'Teßt', 'mode': 'joint', 'sport': 'W', 'level': 'beginner', 'first_day': datetime.date(2019, 3, 1), 'country': 'DE', - 'trainer_firstname': u'Übungsleiter', - 'trainer_familyname': u'Weißalles', + 'trainer_firstname': 'Übungsleiter', + 'trainer_familyname': 'Weißalles', 'trainer_email': 'trainer@localhost', } -EVENT_UPDATED_EMAIL_TEMPLATE = u"""Hallo {recipient_first_name}, +EVENT_UPDATED_EMAIL_TEMPLATE = """Hallo {recipient_first_name}, {editor_full_name} hat die folgende Veranstaltung geändert: {event} @@ -53,7 +53,7 @@ Link zur Veranstaltung: Veröffentlichung: sofort """ -EVENT_SUBMITTED_EMAIL_TEMPLATE = u"""Hallo {recipient_first_name}, +EVENT_SUBMITTED_EMAIL_TEMPLATE = """Hallo {recipient_first_name}, eine Veranstaltung wurde in deinem Namen eingereicht. Die entsprechenden Referenten wurden informiert, um deine Veranstaltung frei zu geben. @@ -67,7 +67,7 @@ Ausschreibung: ============== {event_text}""" -EVENT_TO_ACCEPT_EMAIL_TEMPLATE = u"""Hallo {recipient_first_name}, +EVENT_TO_ACCEPT_EMAIL_TEMPLATE = """Hallo {recipient_first_name}, {trainer_firstname} {trainer_familyname} hat eine Veranstaltung eingereicht. @@ -99,7 +99,7 @@ Bearbeitungshinweis: ==================== {internal_note}""" -EVENT_TO_PUBLISH_WEB_EMAIL_TEMPLATE = u"""Hallo {recipient_first_name}, +EVENT_TO_PUBLISH_WEB_EMAIL_TEMPLATE = """Hallo {recipient_first_name}, {trainer_firstname} {trainer_familyname} hat eine neue Veranstaltung eingereicht. Die Veranstaltung wurde von {editor_full_name} zur Veröffentlichung @@ -132,7 +132,7 @@ Joomla HTML

""" -EVENT_TO_PUBLISH_FACEBOOK_EMAIL_TEMPLATE = u"""Hallo {recipient_first_name}, +EVENT_TO_PUBLISH_FACEBOOK_EMAIL_TEMPLATE = """Hallo {recipient_first_name}, {trainer_firstname} {trainer_familyname} hat eine neue Veranstaltung eingereicht. Die Veranstaltung wurde von {editor_full_name} zur Veröffentlichung @@ -154,7 +154,7 @@ Ausschreibung: {event_text}""" -class EmailTestCase(EmailTestMixin, RoleMixin, TestCase): +class EmailTestCase(EmailTestMixin, RoleMixin, EventMixin, TestCase): def setUp(self): super(EmailTestCase, self).setUp() @@ -164,8 +164,7 @@ class EmailTestCase(EmailTestMixin, RoleMixin, TestCase): self.app_settings = app_config.settings event_data = TEST_EVENT_DATA - self.event = Event(**event_data) - self.event.save() + self.event = self.create_event(event_data) self.trainer = self.event.owner self.manager_super = self.create_user_for_role('manager_super', 'password', 'Touren', 'Referent') @@ -177,20 +176,20 @@ class EmailTestCase(EmailTestMixin, RoleMixin, TestCase): model = get_user_model() self.recipient = model.objects.create_user(username='recipient@example.com', email='recipient@example.com', - password=u'mellon12', - first_name=u'Re Ö.', - last_name=u'Cipient', + password='mellon12', + first_name='Re Ö.', + last_name='Cipient', ) self.editor = model.objects.create_user(username='editor@example.com', email='editor@example.com', - password=u'mellon12', - first_name=u'Ed Ü.', - last_name=u'Itor', + password='mellon12', + first_name='Ed Ü.', + last_name='Itor', ) def test_disabled_mail(self): self.event.workflow.update_status('submitted', self.trainer) - self.event.description += u' Gößweinstein' + self.event.description += ' Gößweinstein' self.event.save() self.event.workflow.update_status('accepted', self.editor) self.event.first_day += datetime.timedelta(1) @@ -203,14 +202,14 @@ class EmailTestCase(EmailTestMixin, RoleMixin, TestCase): self.app_settings.enable_email_on_update = True self.event.workflow.update_status('submitted', self.trainer) - self.event.description += u' Gößweinstein' + self.event.description += ' Gößweinstein' self.event.save() self.assertEqual(len(django_mail.outbox), 2) mail = django_mail.outbox[0] self.assertSender(mail) self.assertRecipients(mail, [expected_recipient]) - self.assertSubject(mail, u'Veranstaltung geändert') + self.assertSubject(mail, 'Veranstaltung geändert') expected_body = EVENT_UPDATED_EMAIL_TEMPLATE.format( recipient_first_name=expected_recipient.first_name, @@ -231,7 +230,7 @@ class EmailTestCase(EmailTestMixin, RoleMixin, TestCase): mail = django_mail.outbox[0] self.assertSender(mail) self.assertRecipients(mail, [expected_recipient]) - self.assertSubject(mail, u'Veranstaltung eingereicht') + self.assertSubject(mail, 'Veranstaltung eingereicht') expected_body = EVENT_SUBMITTED_EMAIL_TEMPLATE.format( recipient_first_name=expected_recipient.first_name, @@ -251,7 +250,7 @@ class EmailTestCase(EmailTestMixin, RoleMixin, TestCase): mail = django_mail.outbox[1] self.assertSender(mail) self.assertRecipients(mail, [expected_recipient]) - self.assertSubject(mail, u'Veranstaltung freigeben') + self.assertSubject(mail, 'Veranstaltung freigeben') action_parameters = '{},{},{}'.format(self.event.id, 'accepted', expected_recipient.id) action = OneClickAction.objects.get(parameters=action_parameters) @@ -272,9 +271,9 @@ class EmailTestCase(EmailTestMixin, RoleMixin, TestCase): editor = self.manager_w self.event.workflow.update_status('submitted', self.trainer) - self.event.internal_note = u'Automatischer Software Test.' + self.event.internal_note = 'Automatischer Software Test.' self.event.planned_publication_date = datetime.date(2019, 3, 24) - ppd_string = u'Sonntag, 24. März 2019' + ppd_string = 'Sonntag, 24. März 2019' self.event.save() self.app_settings.enable_email_on_status_update = True @@ -284,7 +283,7 @@ class EmailTestCase(EmailTestMixin, RoleMixin, TestCase): mail = django_mail.outbox[0] self.assertSender(mail) self.assertRecipients(mail, [expected_recipient]) - self.assertSubject(mail, u'{}: Veranstaltung freigegeben'.format(self.event.number)) + self.assertSubject(mail, '{}: Veranstaltung freigegeben'.format(self.event.number)) expected_body = EVENT_ACCEPTED_EMAIL_TEMPLATE.format( recipient_first_name=expected_recipient.first_name, @@ -302,9 +301,9 @@ class EmailTestCase(EmailTestMixin, RoleMixin, TestCase): editor = self.manager_w self.event.workflow.update_status('submitted', self.trainer) - self.event.internal_note = u'Automatischer Software Test.' + self.event.internal_note = 'Automatischer Software Test.' self.event.planned_publication_date = datetime.date(2019, 3, 24) - ppd_string = u'Sonntag, 24. März 2019' + ppd_string = 'Sonntag, 24. März 2019' self.event.save() self.app_settings.enable_email_on_status_update = True @@ -314,7 +313,7 @@ class EmailTestCase(EmailTestMixin, RoleMixin, TestCase): mail = django_mail.outbox[1] self.assertSender(mail) self.assertRecipients(mail, [expected_recipient]) - self.assertSubject(mail, u'{}: Veranstaltung veröffentlichen'.format(self.event.number)) + self.assertSubject(mail, '{}: Veranstaltung veröffentlichen'.format(self.event.number)) action_parameters = '{},{},{}'.format(self.event.id, 'publishing_web', expected_recipient.id) action = OneClickAction.objects.get(parameters=action_parameters) @@ -352,7 +351,7 @@ class EmailTestCase(EmailTestMixin, RoleMixin, TestCase): mail = django_mail.outbox[2] self.assertSender(mail) self.assertRecipients(mail, [expected_recipient]) - self.assertSubject(mail, u'{}: Veranstaltung veröffentlichen'.format(self.event.number)) + self.assertSubject(mail, '{}: Veranstaltung veröffentlichen'.format(self.event.number)) action_parameters = '{},{},{}'.format(self.event.id, 'publishing_facebook', expected_recipient.id) action = OneClickAction.objects.get(parameters=action_parameters) diff --git a/dav_events/tests/test_oneclickactions.py b/dav_events/tests/test_oneclickactions.py new file mode 100644 index 0000000..3cad3a2 --- /dev/null +++ b/dav_events/tests/test_oneclickactions.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +import datetime +import re +from django.apps import apps +from django.test import TestCase +from django.urls import reverse +from django.utils.translation import ugettext + +from dav_base.tests.generic import EmailTestMixin + +from ..models.oneclickaction import OneClickAction + +from .generic import RoleMixin, EventMixin + + +TEST_EVENT_DATA = { + 'title': 'Täst', + 'description': 'Teßt', + 'mode': 'joint', + 'sport': 'W', + 'level': 'beginner', + 'first_day': datetime.date(2019, 3, 1), + 'country': 'DE', + 'trainer_firstname': 'Übungsleiter', + 'trainer_familyname': 'Weißalles', + 'trainer_email': 'trainer@localhost', +} + + +class ActionTestCase(EmailTestMixin, RoleMixin, EventMixin, TestCase): + def _get_oneclick_url_from_mail(self, mail): + uuid_pattern = '[a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12}' + pseudo_uuid = 'abcdef12-1234-abcd-1234-abcdef123456' + pseudo_url_path = reverse('dav_events:action_run', kwargs={'pk': pseudo_uuid}) + url_path_pattern = pseudo_url_path.replace(pseudo_uuid, uuid_pattern) + url_pattern = r'{}({})'.format(self.email_base_url, url_path_pattern) + + match = re.search(url_pattern, mail.body) + if match: + url = match.groups()[0] + else: # pragma: no cover + url = None + return url + + def _get_uuid_from_url(self, url): + uuid_pattern = r'([a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12})' + + match = re.search(uuid_pattern, url) + if match: + uuid = match.groups()[0] + else: # pragma: no cover + uuid = None + return uuid + + def _test_status_update(self, status_code, user): + event = self.create_event(TEST_EVENT_DATA) + self.submit_event(event) + + if status_code in ('publishing_web', 'publishing_facebook'): + self.accept_event(event, user) + + mails = self.get_mail_for_user(user) + self.assertEqual(len(mails), 1) + mail = mails[0] + + action_url = self._get_oneclick_url_from_mail(mail) + self.assertNotEqual(action_url, None) + + uuid = self._get_uuid_from_url(action_url) + self.assertNotEqual(uuid, None) + + action = OneClickAction.objects.get(id=uuid) + action_command = 'EVENT_STATUS_UPDATE' + action_parameters = [str(event.id), status_code, str(user.id)] + self._assertAction(action, command=action_command, parameters=action_parameters, url=action_url) + + response = self.client.get(action_url) + self.assertEqual(response.status_code, 200) + + content = response.content.decode('utf-8') + status_label = self.get_status_label(status_code) + message = ugettext('Der Status wurde auf \'%(status)s\' gesetzt.') % {'status': status_label} + html = message.replace('\'', ''') + self.assertInHTML(html, content) + self.assertRegex(content, r'alert-success') + + self.assertTrue(event.workflow.has_reached_status(status_code)) + action.refresh_from_db() + self.assertTrue(action.done) + + def _assertAction(self, action, command=None, parameters=None, url=None): + if command: + self.assertEqual(action.command, command) + if parameters: + if isinstance(parameters, list): + parameters = ','.join(parameters) + self.assertEqual(action.parameters, parameters) + if url: + self.assertEqual(action.get_absolute_url(), url) + + def _assertRepeated(self, response): + self.assertEqual(response.status_code, 200) + + content = response.content.decode('utf-8') + html = ugettext('Diese Aktion hast du bereits ausgeführt.') + self.assertInHTML(html, content) + self.assertRegex(content, r'alert-warning') + + def _assertAlready(self, response, status_code, user): + self.assertEqual(response.status_code, 200) + + content = response.content.decode('utf-8') + status_label = self.get_status_label(status_code) + message = (ugettext('Der Status wurde bereits' + ' am %(date)s' + ' von %(user)s' + ' auf \'%(status)s\' gesetzt.') % { + 'status': status_label, + 'date': datetime.datetime.now().strftime('%d.%m.%Y %H:%M:%S'), + 'user': user.get_full_name(), + }) + html = message.replace('\'', ''') + self.assertInHTML(html, content) + self.assertRegex(content, r'alert-success') + + def setUp(self): + super(ActionTestCase, self).setUp() + + app_config = apps.get_containing_app_config(__package__) + app_config.settings.enable_email_on_status_update = True + + self.manager_super = self.create_user_for_role('manager_super', 'password', 'Touren', 'Referent') + self.manager_w = self.create_user_for_role('manager_w', 'password', 'Bereichsleiter', 'Wandern') + self.publisher_web = self.create_user_for_role('publisher_web', 'password', 'Joomla', 'Redakteur') + self.publisher_facebook = self.create_user_for_role('publisher_facebook', 'password', 'Facebook', 'Redakteur') + + def test_accept_by_manager_super(self): + self._test_status_update('accepted', self.manager_super) + + def test_accept_by_manager_sub(self): + self._test_status_update('accepted', self.manager_w) + + def test_accept_repeated(self): + event = self.create_event(TEST_EVENT_DATA) + self.submit_event(event) + + action_parameters = '{},accepted,{}'.format(event.id, self.manager_super.id) + action = OneClickAction.objects.get(command='EVENT_STATUS_UPDATE', parameters=action_parameters) + action_url = action.get_absolute_url() + + self.client.get(action_url) + response = self.client.get(action_url) + self._assertRepeated(response) + + def test_accepted_already(self): + event = self.create_event(TEST_EVENT_DATA) + self.submit_event(event) + + action_parameters = '{},accepted,{}'.format(event.id, self.manager_super.id) + action = OneClickAction.objects.get(command='EVENT_STATUS_UPDATE', parameters=action_parameters) + action_url = action.get_absolute_url() + self.client.get(action_url) + + action_parameters = '{},accepted,{}'.format(event.id, self.manager_w.id) + action = OneClickAction.objects.get(command='EVENT_STATUS_UPDATE', parameters=action_parameters) + action_url = action.get_absolute_url() + response = self.client.get(action_url) + + self._assertAlready(response, 'accepted', self.manager_super) + + def test_publishing_by_publisher_web(self): + self._test_status_update('publishing_web', self.publisher_web) + + def test_publishing_by_publisher_facebook(self): + self._test_status_update('publishing_facebook', self.publisher_facebook) diff --git a/dav_events/tests/test_screenshots.py b/dav_events/tests/test_screenshots.py index f570ead..44ec355 100644 --- a/dav_events/tests/test_screenshots.py +++ b/dav_events/tests/test_screenshots.py @@ -427,7 +427,7 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): self.wait_until_stale(c, button) except NoSuchElementException: break - else: + else: # pragma: no cover self.fail('Too many sub forms') if 'internal_note' in data: @@ -500,7 +500,7 @@ class TestCase(SeleniumAuthMixin, RoleMixin, ScreenshotTestCase): button.click() self.wait_until_stale(c, button) - else: + else: # pragma: no cover self.fail('Too many sub forms') if screenshots: