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>') | 
