mirror of
https://github.com/jlengrand/bugsink.git
synced 2026-03-10 08:01:17 +00:00
Sending of emails: tests, .txt versions, further small improvements
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
from celery import shared_task
|
||||
|
||||
from django.conf import settings
|
||||
from django.template.defaultfilters import truncatechars
|
||||
|
||||
from projects.models import ProjectMembership
|
||||
from issues.models import Issue
|
||||
|
||||
from .utils import send_rendered_email
|
||||
|
||||
@@ -13,24 +15,36 @@ def _get_users_for_email_alert(issue):
|
||||
|
||||
@shared_task
|
||||
def send_new_issue_alert(issue_id):
|
||||
issue = Issue.objects.get(id=issue_id)
|
||||
for membership in _get_users_for_email_alert(issue):
|
||||
send_rendered_email(
|
||||
subject=f"New issue: {issue.title()}",
|
||||
base_template_name="alerts/new_issue",
|
||||
recipient_list=[membership.user.email],
|
||||
context={
|
||||
"issue": issue,
|
||||
"project": issue.project,
|
||||
},
|
||||
)
|
||||
_send_alert(issue_id, "New issue:", "a", "NEW")
|
||||
|
||||
|
||||
@shared_task
|
||||
def send_regression_alert(issue_id):
|
||||
raise NotImplementedError("TODO")
|
||||
_send_alert(issue_id, "Regression:", "a", "REGRESSED")
|
||||
|
||||
|
||||
@shared_task
|
||||
def send_unmute_alert(issue_id):
|
||||
raise NotImplementedError("TODO")
|
||||
_send_alert(issue_id, "Unmuted issue:", "an", "UNMUTED")
|
||||
|
||||
|
||||
def _send_alert(issue_id, subject_prefix, alert_article, alert_reason):
|
||||
from issues.models import Issue # avoid circular import
|
||||
|
||||
issue = Issue.objects.get(id=issue_id)
|
||||
for membership in _get_users_for_email_alert(issue):
|
||||
send_rendered_email(
|
||||
subject=truncatechars(f"{subject_prefix} {issue.title()} in {issue.project.name}", 100),
|
||||
base_template_name="alerts/issue_alert",
|
||||
recipient_list=[membership.user.email],
|
||||
context={
|
||||
"site_name": settings.SITE_NAME,
|
||||
"base_url": settings.BASE_URL + "/",
|
||||
"issue_title": issue.title(),
|
||||
"project_name": issue.project.name,
|
||||
"issue_url": settings.BASE_URL + issue.get_absolute_url(),
|
||||
"alert_article": alert_article,
|
||||
"alert_reason": alert_reason,
|
||||
"settings_url": settings.BASE_URL + "/", # TODO
|
||||
},
|
||||
)
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
<tr>
|
||||
<td class="email-masthead" style="word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px; text-align: center; padding: 25px 0;" align="center">
|
||||
<a href="{{ base_url }}" class="f-fallback email-masthead_name" style="color: #A8AAAF; font-size: 16px; font-weight: bold; text-decoration: none; text-shadow: 0 1px 0 white;">
|
||||
Bugsink
|
||||
{{ site_name }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -470,14 +470,14 @@
|
||||
<tr>
|
||||
<td class="email-body" width="570" cellpadding="0" cellspacing="0" style="word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px; width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0; padding: 0;">
|
||||
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation" style="width: 570px; -premailer-width: 570px; -premailer-cellpadding: 0; -premailer-cellspacing: 0; background-color: #FFFFFF; margin: 0 auto; padding: 0;" bgcolor="#FFFFFF">
|
||||
{% Body content #}
|
||||
{# Body content #}
|
||||
<tr>
|
||||
<td class="content-cell" style="word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px; padding: 45px;">
|
||||
<div class="f-fallback">
|
||||
<h1 style="margin-top: 0; color: #333333; font-size: 22px; font-weight: bold; text-align: left;" align="left">{{ issue_title }}</h1>
|
||||
<h1 style="margin-top: 0; color: #333333; font-size: 22px; font-weight: bold; text-align: left;" align="left">{{ issue_title|truncatechars:100 }}</h1>
|
||||
|
||||
<p style="font-size: 16px; line-height: 1.625; color: #51545E; margin: 1.1875em 0 1.1875em;">
|
||||
This is a <strong>NEW</strong> issue on project <strong>"{{ project_name }}"</strong>.
|
||||
This is {{ alert_article }} <strong>{{ alert_reason }}</strong> issue on project <strong>"{{ project_name }}"</strong>.
|
||||
</p>
|
||||
|
||||
{# Action #}
|
||||
@@ -488,19 +488,22 @@
|
||||
<table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
|
||||
<tr>
|
||||
<td align="center" style="word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px;">
|
||||
<a href="{{ issue_url }}" class="f-fallback button button--green" target="_blank" style="color: #51545E; background-color: #A5F3FC; display: inline-block; text-decoration: none; border-radius: 3px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); -webkit-text-size-adjust: none; box-sizing: border-box; border-color: #A5F3FC; border-style: solid; border-width: 10px 18px;"><b>View on Bugsink</b></a>
|
||||
<a href="{{ issue_url }}" class="f-fallback button button--green" target="_blank" style="color: #51545E; background-color: #A5F3FC; display: inline-block; text-decoration: none; border-radius: 3px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); -webkit-text-size-adjust: none; box-sizing: border-box; border-color: #A5F3FC; border-style: solid; border-width: 10px 18px;"><b>View on {{ site_name }}</b></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
{# Sub copy #}
|
||||
<table class="body-sub" role="presentation" style="margin-top: 25px; padding-top: 25px; border-top-width: 1px; border-top-color: #EAEAEC; border-top-style: solid;">
|
||||
<tr>
|
||||
<td style="word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px;">
|
||||
<p class="f-fallback sub" style="font-size: 13px; line-height: 1.625; color: #51545E; margin: .4em 0 1.1875em;">If you’re having trouble with the button above, copy and paste the URL below into your web browser.</p>
|
||||
<p class="f-fallback sub" style="font-size: 13px; line-height: 1.625; color: #51545E; margin: .4em 0 0;">{{ issue_url }}</p>
|
||||
<p class="f-fallback sub" style="font-size: 13px; line-height: 1.625; color: #51545E; margin: .4em 0 0;">Copy/paste link:</p>
|
||||
<p class="f-fallback sub" style="font-size: 13px; line-height: 1.625; color: #51545E; margin: 0 0 1.1875em;">{{ issue_url }}</p>
|
||||
<p class="f-fallback sub" style="font-size: 13px; line-height: 1.625; color: #51545E; margin: .4em 0 0;"><a href="{{ settings_url }}">Manage notification settings</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -1,3 +1,83 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
from django.core import mail
|
||||
from django.contrib.auth.models import User
|
||||
from django.template.loader import get_template
|
||||
|
||||
from issues.factories import get_or_create_issue
|
||||
from projects.models import Project, ProjectMembership
|
||||
from events.factories import create_event
|
||||
|
||||
from .tasks import send_new_issue_alert, send_regression_alert, send_unmute_alert
|
||||
from .views import DEBUG_CONTEXTS
|
||||
|
||||
|
||||
class TestAlertSending(TestCase):
|
||||
|
||||
def test_send_new_issue_alert(self):
|
||||
project = Project.objects.create(name="Test project")
|
||||
|
||||
user = User.objects.create_user(username="testuser", email="test@example.org")
|
||||
ProjectMembership.objects.create(
|
||||
project=project,
|
||||
user=user,
|
||||
send_email_alerts=True,
|
||||
)
|
||||
|
||||
issue, _ = get_or_create_issue(project=project)
|
||||
create_event(project=project, issue=issue)
|
||||
|
||||
send_new_issue_alert(issue.id)
|
||||
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
|
||||
def test_send_regression_alert(self):
|
||||
project = Project.objects.create(name="Test project")
|
||||
|
||||
user = User.objects.create_user(username="testuser", email="test@example.org")
|
||||
ProjectMembership.objects.create(
|
||||
project=project,
|
||||
user=user,
|
||||
send_email_alerts=True,
|
||||
)
|
||||
|
||||
issue, _ = get_or_create_issue(project=project)
|
||||
create_event(project=project, issue=issue)
|
||||
|
||||
send_regression_alert(issue.id)
|
||||
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
|
||||
def test_send_unmute_alert(self):
|
||||
project = Project.objects.create(name="Test project")
|
||||
|
||||
user = User.objects.create_user(username="testuser", email="test@example.org")
|
||||
ProjectMembership.objects.create(
|
||||
project=project,
|
||||
user=user,
|
||||
send_email_alerts=True,
|
||||
)
|
||||
|
||||
issue, _ = get_or_create_issue(project=project)
|
||||
create_event(project=project, issue=issue)
|
||||
|
||||
send_unmute_alert(issue.id)
|
||||
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
|
||||
def test_txt_and_html_have_relevant_variables_defined(self):
|
||||
example_context = DEBUG_CONTEXTS["issue_alert"]
|
||||
html_template = get_template("alerts/issue_alert.html")
|
||||
text_template = get_template("alerts/issue_alert.txt")
|
||||
|
||||
unused_in_text = [
|
||||
"base_url", # link to the site is not included at the top of the text template
|
||||
]
|
||||
|
||||
for type_, template in [("html", html_template), ("text", text_template)]:
|
||||
for variable in example_context.keys():
|
||||
if type_ == "text" and variable in unused_in_text:
|
||||
continue
|
||||
|
||||
self.assertTrue(
|
||||
"{{ %s" % variable in template.template.source, "'{{ %s ' not in %s template" % (variable, type_))
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.template import Context
|
||||
from django.template.loader import get_template
|
||||
|
||||
|
||||
@@ -7,8 +6,8 @@ def send_rendered_email(subject, base_template_name, recipient_list, context=Non
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
html_content = get_template(base_template_name + ".html").render(Context(context))
|
||||
text_content = get_template(base_template_name + ".txt").render(Context(context))
|
||||
html_content = get_template(base_template_name + ".html").render(context)
|
||||
text_content = get_template(base_template_name + ".txt").render(context)
|
||||
|
||||
# Configure and send an EmailMultiAlternatives
|
||||
msg = EmailMultiAlternatives(
|
||||
|
||||
@@ -2,11 +2,15 @@ from django.shortcuts import render
|
||||
from django.conf import settings
|
||||
|
||||
DEBUG_CONTEXTS = {
|
||||
"new_issue": {
|
||||
"issue_alert": {
|
||||
"site_name": settings.SITE_NAME,
|
||||
"base_url": settings.BASE_URL + "/",
|
||||
"issue_title": "AttributeError: 'NoneType' object has no attribute 'data'",
|
||||
"project_name": "My first project",
|
||||
"alert_article": "a",
|
||||
"alert_reason": "NEW",
|
||||
"issue_url": settings.BASE_URL + "/issues/issue/00000000-0000-0000-0000-000000000000/",
|
||||
"settings_url": settings.BASE_URL + "/", # TODO
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user