Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions django/core/mail/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,3 +505,16 @@ def _create_alternatives(self, msg):
)
)
return msg

def body_contains(self, text):
"""
Checks that ``text`` occurs in the email body and in all attached MIME
type text/* alternatives.
"""
if text not in self.body:
return False

for content, mimetype in self.alternatives:
if mimetype.startswith("text/") and text not in content:
return False
return True
4 changes: 4 additions & 0 deletions docs/releases/5.2.txt
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ Email
<django.core.mail.EmailMultiAlternatives.alternatives>` is now a list of
named tuples, as opposed to regular tuples.

* The new :meth:`~django.core.mail.EmailMultiAlternatives.body_contains` method
returns a boolean indicating whether a provided text is contained in the
email ``body`` and in all attached MIME type ``text/*`` alternatives.

Error Reporting
~~~~~~~~~~~~~~~

Expand Down
20 changes: 20 additions & 0 deletions docs/topics/email.txt
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,26 @@ Django's email library, you can do this using the
msg.attach_alternative(html_content, "text/html")
msg.send()

.. method:: body_contains(text)

.. versionadded:: 5.2

Returns a boolean indicating whether the provided ``text`` is
contained in the email ``body`` and in all attached MIME type
``text/*`` alternatives.

This can be useful when testing emails. For example::

def test_contains_email_content(self):
subject = "Hello World"
from_email = "from@example.com"
to = "to@example.com"
msg = EmailMultiAlternatives(subject, "I am content.", from_email, [to])
msg.attach_alternative("<p>I am content.</p>", "text/html")

self.assertIs(msg.body_contains("I am content"), True)
self.assertIs(msg.body_contains("<p>I am content.</p>"), False)

Updating the default content type
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
18 changes: 18 additions & 0 deletions tests/mail/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1163,6 +1163,24 @@ def test_email_multi_alternatives_content_mimetype_none(self):
with self.assertRaisesMessage(ValueError, msg):
email_msg.attach_alternative("<p>content</p>", None)

def test_body_contains(self):
email_msg = EmailMultiAlternatives()
email_msg.body = "I am content."
self.assertIs(email_msg.body_contains("I am"), True)
self.assertIs(email_msg.body_contains("I am content."), True)

email_msg.attach_alternative("<p>I am different content.</p>", "text/html")
self.assertIs(email_msg.body_contains("I am"), True)
self.assertIs(email_msg.body_contains("I am content."), False)
self.assertIs(email_msg.body_contains("<p>I am different content.</p>"), False)

def test_body_contains_alternative_non_text(self):
email_msg = EmailMultiAlternatives()
email_msg.body = "I am content."
email_msg.attach_alternative("I am content.", "text/html")
email_msg.attach_alternative(b"I am a song.", "audio/mpeg")
self.assertIs(email_msg.body_contains("I am content"), True)


@requires_tz_support
class MailTimeZoneTests(SimpleTestCase):
Expand Down