3 # Copyright (C) 2012 FinalsClub Foundation
6 from django.test import TestCase
7 from bs4 import BeautifulSoup
8 from karmaworld.apps.notes.search import SearchIndex
10 from karmaworld.apps.notes.models import Note, NoteMarkdown
11 from karmaworld.apps.notes import sanitizer
12 from karmaworld.apps.courses.models import Course
13 from karmaworld.apps.courses.models import School
15 class TestNotes(TestCase):
18 # create base values to test db representations
19 self.now = datetime.datetime.utcnow()
21 # create a school to satisfy course requirements
22 self.school = School()
23 self.school.name = 'Marshall College'
26 # create a course to test relationships
27 self.course = Course()
28 self.course.school = self.school
29 self.course.name = u'Archaeology 101'
31 # override Course.save() appending an ID to the slug
32 self.course.slug = u'archaeology-101'
35 # create a note to test against
37 self.note.course = self.course
38 self.note.name = u"Lecture notes concerning the use of therefore ∴"
39 self.note.category = Note.LECTURE_NOTES
40 self.note.uploaded_at = self.now
41 self.note.text = "This is the plaintext version of a note. It's pretty cool. Alpaca."
44 def test_course_fkey(self):
45 self.assertEqual(self.course, self.note.course)
47 def test_slug_natural(self):
48 """ Test that the slug field is slugifying unicode Note.names """
49 expected = u"lecture-notes-concerning-the-use-of-therefore"
50 self.assertEqual(self.note.slug, expected)
52 def test_remake_slug(self):
53 """ Test the generation of a Note.slug field based on Note.
54 Name collision is expected, so see if slug handles this."""
55 expected = u"lecture-notes-concerning-the-use-of-therefore-{0}-{1}-{2}".format(
56 self.note.uploaded_at.month,
57 self.note.uploaded_at.day, self.note.uploaded_at.microsecond)
61 self.assertEqual(self.note.slug, expected)
63 expected_url_prefix = u'/note/marshall-college/archaeology-101/'
64 expected_slug = u'lecture-notes-concerning-the-use-of-therefore'
65 expected = expected_url_prefix + expected_slug
67 def test_note_get_absolute_url_slug(self):
68 """ Given a note with a slug, test that an expected url is generated """
69 # check that Note.get_absolute_url() is generating the right url
70 self.assertEqual(self.note.get_absolute_url(), self.expected)
72 def test_note_get_absolute_url_id(self):
74 url = self.expected_url_prefix + str(self.note.id)
75 self.assertEqual(self.note.get_absolute_url(), url)
77 def test_note_markdown_rendering(self):
78 rich = NoteMarkdown(note=self.note,
79 markdown="""# This is fun\n[oh](http://yeah.com)""")
81 self.assertHTMLEqual(rich.html,
82 """<h1>This is fun</h1>\n<p><a href="http://yeah.com" rel="nofollow" target="_blank">oh</a></p>""")
84 def test_note_rich_text_sanitization(self):
85 rich = NoteMarkdown(note=self.note, html="""
86 <script>unsafe</script>
87 <h1 class='obtrusive'>Something</h1>
91 <a href='javascript:alert("Oh no")'>This stuff</a>
92 <a href='http://google.com'>That guy</a>
96 self.assertHTMLEqual(rich.html, u"""
101 <a target='_blank' rel='nofollow'>This stuff</a>
102 <a href="http://google.com" target="_blank" rel="nofollow">That guy</a>
105 class TestSanitizeToEditable(TestCase):
106 def test_clean(self):
108 <script>unsafe</script>
109 <style>html {background-color: pink !important;}</style>
110 <h1 class='obtrusive'>Something</h1>
114 <a href='javascript:alert("Oh no")'>This stuff</a>
115 <a href='http://google.com'>That guy</a>
117 <h3>This should show up</h3>
121 self.assertHTMLEqual(sanitizer.sanitize_html_to_editable(dirty), u"""
126 <a target="_blank" rel="nofollow">This stuff</a>
127 <a href="http://google.com" target="_blank" rel="nofollow">That guy</a>
128 <h3>This should show up</h3>
131 def test_canonical_rel(self):
132 html = """<h1>Hey there!</h1>"""
133 canonicalized = sanitizer.set_canonical_rel(html, "http://example.com")
134 self.assertHTMLEqual(canonicalized, """<html><head><link rel='canonical' href='http://example.com'></head><body><h1>Hey there!</h1></body></html>""")
136 def test_data_uri(self):
137 # Strip out all data URIs.
138 html = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">'
139 self.assertHTMLEqual(sanitizer.sanitize_html_to_editable(html), "<img/>")
141 # Strip out non-image data URI's
142 html = '<img src="data:application/pdf;base64,blergh">'
143 self.assertHTMLEqual(sanitizer.sanitize_html_to_editable(html), "<img/>")
145 class TestDataUriToS3(TestCase):
146 def test_image_data_uri(self):
147 html = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">'
148 s3ified = sanitizer.data_uris_to_s3(html)
149 soup = BeautifulSoup(s3ified)
150 regex = r'^https?://.*$'
151 self.assertTrue(bool(re.match(regex, soup.img['src'])),
152 "{} does not match {}".format(s3ified, regex))
154 resanitize = sanitizer.data_uris_to_s3(s3ified)
155 self.assertHTMLEqual(s3ified, resanitize)
157 def test_font_face_data_uri(self):
158 # Note: this data-uri is not a valid font (it's the red dot).
159 html = '''<style>@font-face { src: url('data:application/font-woff;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='); }</style>'''
161 s3ified = sanitizer.data_uris_to_s3(html)
162 self.assertFalse(re.search(r"url\('data:application", s3ified),
163 "data URL not removed: {}".format(s3ified))
164 self.assertTrue(re.search(r"url\('https?://[^\)]+\)", s3ified),
165 "URL not inserted: {}".format(s3ified))
167 # Ensure that cleaning is idempotent.
168 self.assertHTMLEqual(s3ified,
169 sanitizer.sanitize_html_preserve_formatting(s3ified))