Added {% embed <instance> %} syntax for embedding. Switched to actually using the...
[philo.git] / tests.py
1 from django.test import TestCase
2 from django import template
3 from django.conf import settings
4 from django.template import loader
5 from django.template.loaders import cached
6 from philo.exceptions import AncestorDoesNotExist
7 from philo.models import Node, Page, Template
8 from philo.contrib.penfield.models import Blog, BlogView, BlogEntry
9 import sys, traceback
10
11
12 class TemplateTestCase(TestCase):
13         fixtures = ['test_fixtures.json']
14         
15         def test_templates(self):
16                 "Tests to make sure that embed behaves with complex includes and extends"
17                 template_tests = self.get_template_tests()
18                 
19                 # Register our custom template loader. Shamelessly cribbed from django core regressiontests.
20                 def test_template_loader(template_name, template_dirs=None):
21                         "A custom template loader that loads the unit-test templates."
22                         try:
23                                 return (template_tests[template_name][0] , "test:%s" % template_name)
24                         except KeyError:
25                                 raise template.TemplateDoesNotExist, template_name
26                 
27                 cache_loader = cached.Loader(('test_template_loader',))
28                 cache_loader._cached_loaders = (test_template_loader,)
29                 
30                 old_template_loaders = loader.template_source_loaders
31                 loader.template_source_loaders = [cache_loader]
32                 
33                 # Turn TEMPLATE_DEBUG off, because tests assume that.
34                 old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False
35                 
36                 # Set TEMPLATE_STRING_IF_INVALID to a known string.
37                 old_invalid = settings.TEMPLATE_STRING_IF_INVALID
38                 expected_invalid_str = 'INVALID'
39                 
40                 failures = []
41                 
42                 # Run tests
43                 for name, vals in template_tests.items():
44                         xx, context, result = vals
45                         try:
46                                 test_template = loader.get_template(name)
47                                 output = test_template.render(template.Context(context))
48                         except Exception:
49                                 exc_type, exc_value, exc_tb = sys.exc_info()
50                                 if exc_type != result:
51                                         tb = '\n'.join(traceback.format_exception(exc_type, exc_value, exc_tb))
52                                         failures.append("Template test %s -- FAILED. Got %s, exception: %s\n%s" % (name, exc_type, exc_value, tb))
53                                 continue
54                         if output != result:
55                                 failures.append("Template test %s -- FAILED. Expected %r, got %r" % (name, result, output))
56                 
57                 # Cleanup
58                 settings.TEMPLATE_DEBUG = old_td
59                 settings.TEMPLATE_STRING_IF_INVALID = old_invalid
60                 loader.template_source_loaders = old_template_loaders
61                 
62                 self.assertEqual(failures, [], "Tests failed:\n%s\n%s" % ('-'*70, ("\n%s\n" % ('-'*70)).join(failures)))
63         
64         
65         def get_template_tests(self):
66                 # SYNTAX --
67                 # 'template_name': ('template contents', 'context dict', 'expected string output' or Exception class)
68                 blog = Blog.objects.all()[0]
69                 return {
70                         # EMBED INCLUSION HANDLING
71                         
72                         'embed01': ('{{ embedded.title|safe }}', {'embedded': blog}, blog.title),
73                         'embed02': ('{{ embedded.title|safe }}{{ var1 }}{{ var2 }}', {'embedded': blog}, blog.title),
74                         'embed03': ('{{ embedded.title|safe }} is a lie!', {'embedded': blog}, '%s is a lie!' % blog.title),
75                         
76                         # Simple template structure with embed
77                         'simple01': ('{% embed penfield.blog with "embed01" %}{% embed penfield.blog 1 %}Simple{% block one %}{% endblock %}', {'blog': blog}, '%sSimple' % blog.title),
78                         'simple02': ('{% extends "simple01" %}', {}, '%sSimple' % blog.title),
79                         'simple03': ('{% embed penfield.blog with "embed000" %}', {}, settings.TEMPLATE_STRING_IF_INVALID),
80                         'simple04': ('{% embed penfield.blog 1 %}', {}, settings.TEMPLATE_STRING_IF_INVALID),
81                         'simple05': ('{% embed penfield.blog with "embed01" %}{% embed blog %}', {'blog': blog}, blog.title),
82                         
83                         # Kwargs
84                         'kwargs01': ('{% embed penfield.blog with "embed02" %}{% embed penfield.blog 1 var1="hi" var2=lo %}', {'lo': 'lo'}, '%shilo' % blog.title),
85                         
86                         # Filters/variables
87                         'filters01': ('{% embed penfield.blog with "embed02" %}{% embed penfield.blog 1 var1=hi|first var2=lo|slice:"3" %}', {'hi': ["These", "words"], 'lo': 'lower'}, '%sTheselow' % blog.title),
88                         'filters02': ('{% embed penfield.blog with "embed01" %}{% embed penfield.blog entry %}', {'entry': 1}, blog.title),
89                         
90                         # Blocky structure
91                         'block01': ('{% block one %}Hello{% endblock %}', {}, 'Hello'),
92                         'block02': ('{% extends "simple01" %}{% block one %}{% embed penfield.blog 1 %}{% endblock %}', {}, "%sSimple%s" % (blog.title, blog.title)),
93                         'block03': ('{% extends "simple01" %}{% embed penfield.blog with "embed03" %}{% block one %}{% embed penfield.blog 1 %}{% endblock %}', {}, "%sSimple%s is a lie!" % (blog.title, blog.title)),
94                         
95                         # Blocks and includes
96                         'block-include01': ('{% extends "simple01" %}{% embed penfield.blog with "embed03" %}{% block one %}{% include "simple01" %}{% embed penfield.blog 1 %}{% endblock %}', {}, "%sSimple%sSimple%s is a lie!" % (blog.title, blog.title, blog.title)),
97                         'block-include02': ('{% extends "simple01" %}{% block one %}{% include "simple04" %}{% embed penfield.blog with "embed03" %}{% include "simple04" %}{% embed penfield.blog 1 %}{% endblock %}', {}, "%sSimple%s%s is a lie!%s is a lie!" % (blog.title, blog.title, blog.title, blog.title)),
98                 }
99
100
101 class NodeURLTestCase(TestCase):
102         """Tests the features of the node_url template tag."""
103         urls = 'philo.urls'
104         fixtures = ['test_fixtures.json']
105         
106         def setUp(self):
107                 if 'south' in settings.INSTALLED_APPS:
108                         from south.management.commands.migrate import Command
109                         command = Command()
110                         command.handle(all_apps=True)
111                 
112                 self.templates = [
113                                 ("{% node_url %}", "/root/never/"),
114                                 ("{% node_url for node2 %}", "/root/blog/"),
115                                 ("{% node_url as hello %}<p>{{ hello|slice:'1:' }}</p>", "<p>root/never/</p>"),
116                                 ("{% node_url for nodes|first %}", "/root/never/"),
117                                 ("{% node_url with entry %}", settings.TEMPLATE_STRING_IF_INVALID),
118                                 ("{% node_url with entry for node2 %}", "/root/blog/2010/10/20/first-entry"),
119                                 ("{% node_url with tag for node2 %}", "/root/blog/tags/test-tag/"),
120                                 ("{% node_url with date for node2 %}", "/root/blog/2010/10/20"),
121                                 ("{% node_url entries_by_day year=date|date:'Y' month=date|date:'m' day=date|date:'d' for node2 as goodbye %}<em>{{ goodbye|upper }}</em>", "<em>/ROOT/BLOG/2010/10/20</em>"),
122                                 ("{% node_url entries_by_month year=date|date:'Y' month=date|date:'m' for node2 %}", "/root/blog/2010/10"),
123                                 ("{% node_url entries_by_year year=date|date:'Y' for node2 %}", "/root/blog/2010/"),
124                 ]
125                 
126                 nodes = Node.objects.all()
127                 blog = Blog.objects.all()[0]
128                 
129                 self.context = template.Context({
130                         'node': nodes[0],
131                         'node2': nodes[1],
132                         'nodes': nodes,
133                         'entry': BlogEntry.objects.all()[0],
134                         'tag': blog.entry_tags.all()[0],
135                         'date': blog.entry_dates['day'][0]
136                 })
137         
138         def test_nodeurl(self):
139                 for string, result in self.templates:
140                         self.assertEqual(template.Template(string).render(self.context), result)
141
142 class TreePathTestCase(TestCase):
143         urls = 'philo.urls'
144         fixtures = ['test_fixtures.json']
145         
146         def setUp(self):
147                 if 'south' in settings.INSTALLED_APPS:
148                         from south.management.commands.migrate import Command
149                         command = Command()
150                         command.handle(all_apps=True)
151         
152         def test_has_ancestor(self):
153                 root = Node.objects.get(slug='root')
154                 third = Node.objects.get(slug='third')
155                 r1 = Node.objects.get(slug='recursive1')
156                 r2 = Node.objects.get(slug='recursive2')
157                 pr1 = Node.objects.get(slug='postrecursive1')
158                 
159                 # Simple case: straight path
160                 self.assertEqual(third.has_ancestor(root), True)
161                 self.assertEqual(root.has_ancestor(root), False)
162                 self.assertEqual(root.has_ancestor(None), True)
163                 self.assertEqual(third.has_ancestor(None), True)
164                 self.assertEqual(root.has_ancestor(root, inclusive=True), True)
165                 
166                 # Recursive case
167                 self.assertEqual(r1.has_ancestor(r1), True)
168                 self.assertEqual(r1.has_ancestor(r2), True)
169                 self.assertEqual(r2.has_ancestor(r1), True)
170                 self.assertEqual(r2.has_ancestor(None), False)
171                 
172                 # Post-recursive case
173                 self.assertEqual(pr1.has_ancestor(r1), True)
174                 self.assertEqual(pr1.has_ancestor(pr1), False)
175                 self.assertEqual(pr1.has_ancestor(pr1, inclusive=True), True)
176                 self.assertEqual(pr1.has_ancestor(None), False)
177                 self.assertEqual(pr1.has_ancestor(root), False)
178         
179         def test_get_path(self):
180                 root = Node.objects.get(slug='root')
181                 third = Node.objects.get(slug='third')
182                 r1 = Node.objects.get(slug='recursive1')
183                 r2 = Node.objects.get(slug='recursive2')
184                 pr1 = Node.objects.get(slug='postrecursive1')
185                 
186                 # Simple case: straight path to None
187                 self.assertEqual(root.get_path(), 'root')
188                 self.assertEqual(third.get_path(), 'root/never/more/second/third')
189                 
190                 # Recursive case: Looped path to root None
191                 self.assertEqual(r1.get_path(), u'\u2026/recursive1/recursive2/recursive3/recursive1')
192                 self.assertEqual(pr1.get_path(), u'\u2026/recursive3/recursive1/recursive2/recursive3/postrecursive1')
193                 
194                 # Simple error case: straight invalid path
195                 self.assertRaises(AncestorDoesNotExist, root.get_path, root=third)
196                 self.assertRaises(AncestorDoesNotExist, third.get_path, root=pr1)
197                 
198                 # Recursive error case
199                 self.assertRaises(AncestorDoesNotExist, r1.get_path, root=root)
200                 self.assertRaises(AncestorDoesNotExist, pr1.get_path, root=third)