diff options
| -rw-r--r-- | conftest.py | 23 | ||||
| -rw-r--r-- | examples/djangoproject/djangoproject/templates/styleguide.html | 11 | ||||
| -rw-r--r-- | pykss/contrib/django/templatetags/pykss.py | 52 | ||||
| -rw-r--r-- | pykss/exceptions.py | 2 | ||||
| -rw-r--r-- | pykss/parser.py | 6 | ||||
| -rw-r--r-- | pykss/section.py | 2 | ||||
| -rw-r--r-- | setup.py | 9 | ||||
| -rw-r--r-- | tests/templates/django.html | 18 | ||||
| -rw-r--r-- | tests/test_contrib/__init__.py | 0 | ||||
| -rw-r--r-- | tests/test_contrib/test_django/__init__.py | 0 | ||||
| -rw-r--r-- | tests/test_contrib/test_django/test_templatetags/__init__.py | 0 | ||||
| -rw-r--r-- | tests/test_contrib/test_django/test_templatetags/test_pykss.py | 93 |
12 files changed, 202 insertions, 14 deletions
diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..ea7d23d --- /dev/null +++ b/conftest.py @@ -0,0 +1,23 @@ +import os +from django.conf import settings + + +def pytest_configure(config): + PROJECT_ROOT = os.path.dirname(__file__) + + if not settings.configured: + settings.configure( + DATABASES={ + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': ':memory:', + }, + }, + DEBUG=False, + INSTALLED_APPS=[ + 'pykss.contrib.django', + ], + PROJECT_ROOT=PROJECT_ROOT, + TEMPLATE_DEBUG=True, + TEMPLATE_DIRS=[os.path.join(PROJECT_ROOT, 'tests', 'templates')], + ) diff --git a/examples/djangoproject/djangoproject/templates/styleguide.html b/examples/djangoproject/djangoproject/templates/styleguide.html index 05d736d..2aa6500 100644 --- a/examples/djangoproject/djangoproject/templates/styleguide.html +++ b/examples/djangoproject/djangoproject/templates/styleguide.html @@ -2,6 +2,17 @@ {% load pykss %} {% block content %} +{% comment %} + +{% styleguideblock styleguide "1.1" %} +... +{% endstyleguideblock %} + +{% styleguideblock styleguide "1.1" using "custom.html" %} +... +{% endstyleguideblock %} + +{% endcomment %} {% styleguideblock styleguide "1.1" %} <button class="{{ modifier.class_name }}">Example Button</button> {% endstyleguideblock %} diff --git a/pykss/contrib/django/templatetags/pykss.py b/pykss/contrib/django/templatetags/pykss.py index f833ade..c7be567 100644 --- a/pykss/contrib/django/templatetags/pykss.py +++ b/pykss/contrib/django/templatetags/pykss.py @@ -1,5 +1,8 @@ from django import template +from django.conf import settings from django.template.loader import render_to_string +from django.utils.safestring import mark_safe +from django.utils.html import escape register = template.Library() @@ -21,22 +24,29 @@ class StyleGuideBlockNode(template.Node): reference = self.reference.resolve(context) template_name = self.template_name.resolve(context) - section = styleguide.section(reference) + try: + section = styleguide.section(reference) + except Exception as e: + if settings.TEMPLATE_DEBUG: + raise e + return '' - example_html = self.nodelist.render(context) + example_html = self.nodelist.render(context).strip() modifier_examples = [] for modifier in section.modifiers: context.update({'modifier': modifier}) + html = self.nodelist.render(context).strip() modifier_examples.append({ 'modifier': modifier, - 'html': self.nodelist.render(context), + 'html': mark_safe(html), }) output = render_to_string(template_name, { 'section': section, - 'example_html': example_html, + 'example_html': mark_safe(example_html), 'modifier_examples': modifier_examples, + "escaped_html": escape(example_html), }) return output @@ -44,17 +54,37 @@ class StyleGuideBlockNode(template.Node): @register.tag def styleguideblock(parser, token): - bits = token.contents.split() + """ + {% styleguideblock styleguide "1.1" %} + <button class="{{ modifier.class_name }}">Example Button</button> + {% endstyleguideblock %} - if len(bits) != 3: - raise template.TemplateSyntaxError("styleguideblock expected at least two arguments") + {% styleguideblock styleguide "1.1" using "custom.html" %} + <button class="{{ modifier.class_name }}">Example Button</button> + {% endstyleguideblock %} - try: - tag, styleguide, reference, template_name = bits - except ValueError: - tag, styleguide, reference = bits + """ + bits = token.split_contents()[1:] + + if len(bits) < 2: + raise template.TemplateSyntaxError("styleguideblock expected at " + "least two arguments") + + elif len(bits) == 2: + styleguide, reference = bits template_name = '"pykss/styleguideblock.html"' + elif len(bits) >= 3 and bits[2] != 'using': + raise template.TemplateSyntaxError("styleguideblock expected using " + "as the third argument") + + elif len(bits) == 3: + raise template.TemplateSyntaxError("styleguideblock expects a " + "template name after 'using'") + + else: + styleguide, reference, _using, template_name = bits + nodelist = parser.parse(('endstyleguideblock',)) parser.delete_first_token() diff --git a/pykss/exceptions.py b/pykss/exceptions.py new file mode 100644 index 0000000..418b94f --- /dev/null +++ b/pykss/exceptions.py @@ -0,0 +1,2 @@ +class SectionDoesNotExist(Exception): + pass diff --git a/pykss/parser.py b/pykss/parser.py index fe18c9f..382cb1f 100644 --- a/pykss/parser.py +++ b/pykss/parser.py @@ -1,6 +1,7 @@ import os from pykss.comment import CommentParser +from pykss.exceptions import SectionDoesNotExist from pykss.section import Section @@ -33,4 +34,7 @@ class Parser(object): return self._sections def section(self, reference): - return self.sections.get(reference, Section()) + try: + return self.sections[reference] + except KeyError: + raise SectionDoesNotExist('Section "%s" does not exist.' % reference) diff --git a/pykss/section.py b/pykss/section.py index bda7e7e..9a98d0f 100644 --- a/pykss/section.py +++ b/pykss/section.py @@ -14,7 +14,7 @@ reference_re = re.compile('%s ([\d\.]+)' % REFERENCE_START) class Section(object): def __init__(self, comment=None, filename=None): - self.comment = comment + self.comment = comment or '' self.filename = filename def parse(self): @@ -23,5 +23,12 @@ setup( ], zip_safe=False, test_suite='runtests.runtests', - extras_require={'tests': ['pytest', 'flake8']}, + extras_require={ + 'tests': [ + 'Django>=1.5', + 'flake8', + 'mock', + 'pytest', + ], + }, ) diff --git a/tests/templates/django.html b/tests/templates/django.html new file mode 100644 index 0000000..7c52f40 --- /dev/null +++ b/tests/templates/django.html @@ -0,0 +1,18 @@ +{# it's probably a terrible idea to render JSON by hand... #} +{ + "section": "{{ section.section }}", + "filename": "{{ section.filename }}", + "description": "{{ section.description }}", + "modifiers": [ + {% for modifier in section.modifiers %} + ["{{ modifier.name }}","{{ modifier.description }}"]{% if not forloop.last %},{% endif %} + {% endfor %} + ], + "example_html": "{{ example_html|escapejs }}", + "modifier_examples": [ + {% for example in modifier_examples %} + ["{{ example.modifier.name }}","{{ example.html|escapejs }}"]{% if not forloop.last %},{% endif %} + {% endfor %} + ], + "escaped_html": "{{ escaped_html|escapejs }}" +} diff --git a/tests/test_contrib/__init__.py b/tests/test_contrib/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/test_contrib/__init__.py diff --git a/tests/test_contrib/test_django/__init__.py b/tests/test_contrib/test_django/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/test_contrib/test_django/__init__.py diff --git a/tests/test_contrib/test_django/test_templatetags/__init__.py b/tests/test_contrib/test_django/test_templatetags/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/test_contrib/test_django/test_templatetags/__init__.py diff --git a/tests/test_contrib/test_django/test_templatetags/test_pykss.py b/tests/test_contrib/test_django/test_templatetags/test_pykss.py new file mode 100644 index 0000000..1f0d967 --- /dev/null +++ b/tests/test_contrib/test_django/test_templatetags/test_pykss.py @@ -0,0 +1,93 @@ +import os + +from django.conf import settings +from django.template import Context, Template, TemplateSyntaxError +from django.test import TestCase +from django.utils import simplejson + +from mock import patch, ANY + +from pykss.exceptions import SectionDoesNotExist +from pykss.parser import Parser + + +class StyleGuideBlockTestCase(TestCase): + + def setUp(self): + css = os.path.join(settings.PROJECT_ROOT, 'tests', 'fixtures', 'css') + self.styleguide = Parser(css) + + def test_when_section_does_not_exist_and_in_debug(self): + template = Template(""" + {% load pykss %} + {% styleguideblock styleguide "99" %} + {% endstyleguideblock %} + """) + context = Context({'styleguide': self.styleguide}) + self.assertRaises(SectionDoesNotExist, template.render, context) + + def test_when_section_does_not_exist_and_not_in_debug(self): + with self.settings(TEMPLATE_DEBUG=False): + template = Template(""" + {% load pykss %} + {% styleguideblock styleguide "99" %} + {% endstyleguideblock %} + """) + context = Context({'styleguide': self.styleguide}) + results = template.render(context) + self.assertEqual(results.strip(), '') + + @patch('pykss.contrib.django.templatetags.pykss.render_to_string') + def test_uses_default_template(self, mock_render_to_string): + template = Template(""" + {% load pykss %} + {% styleguideblock styleguide "2.1.1" %} + {% endstyleguideblock %} + """) + context = Context({'styleguide': self.styleguide}) + template.render(context) + mock_render_to_string.assert_called_with('pykss/styleguideblock.html', ANY) + + @patch('pykss.contrib.django.templatetags.pykss.render_to_string') + def test_allows_overiding_template(self, mock_render_to_string): + template = Template(""" + {% load pykss %} + {% styleguideblock styleguide "2.1.1" using "custom.html" %} + {% endstyleguideblock %} + """) + context = Context({'styleguide': self.styleguide}) + template.render(context) + mock_render_to_string.assert_called_with('custom.html', ANY) + + def test_when_using_argument_is_wrong(self): + text = """ + {% load pykss %} + {% styleguideblock styleguide "2.1.1" "custom.html" %} + {% endstyleguideblock %} + """ + self.assertRaises(TemplateSyntaxError, Template, text) + + def test_when_using_is_defined_without_template(self): + text = """ + {% load pykss %} + {% styleguideblock styleguide "2.1.1" using %} + {% endstyleguideblock %} + """ + self.assertRaises(TemplateSyntaxError, Template, text) + + def test_renders_correctly(self): + template = Template(""" + {% load pykss %} + {% styleguideblock styleguide "2.1.1" using "django.html" %} + <i class="{{ modifier.class_name }}"></i> + {% endstyleguideblock %} + """) + context = Context({'styleguide': self.styleguide}) + results = simplejson.loads(template.render(context)) + self.assertEqual(results['section'], '2.1.1') + self.assertEqual(results['filename'], 'buttons.css') + self.assertEqual(results['description'], 'Your standard form button.') + self.assertEqual(results['modifiers'][0], [':hover', 'Highlights when hovering.']) + self.assertEqual(results['example_html'], '<i class=""></i>') + self.assertEqual(results['modifier_examples'][0], [':hover', '<i class="pseudo-class-hover"></i>']) + self.assertEqual(results['escaped_html'], '<i class=""></i>') |
