<reponame>czubik8805/django-markupmirror
from __future__ import absolute_import, unicode_literals

try:
    import json
except ImportError:
    import simplejson as json

import textwrap

from django.core import serializers
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase

from markupmirror.fields import Markup
from markupmirror.fields import MarkupMirrorField
from markupmirror.markup.base import markup_pool

from tests.models import Concrete, Post, PostForm

try:
    import json
except ImportError:
    import simplejson as json


__all__ = ('MarkupMirrorFieldTests',)


class MarkupMirrorFieldTests(TestCase):
    """Tests the ``markupmirror.fields.MarkupMirrorField`` implementation."""

    def setUp(self):
        """Creates three ``Post`` objects with different field settings."""

        # Post with Markdown field
        self.mp = Post(title="example Markdown post",
                       body="**markdown**", body_markup_type='markdown')
        self.mp.save()

        # Post with reStructuredText field
        self.rp = Post(title="example reStructuredText post",
                       body="*reST*", body_markup_type='restructuredtext')
        self.rp.save()

        # Post being attacked
        self.xss_str = "<script>alert('xss');</script>"
        self.xss_post = Post(title="example XSS post",
                             body=self.xss_str, body_markup_type='markdown',
                             comment=self.xss_str)
        self.xss_post.save()

    def test_verbose_name(self):
        """Tests that the standard field property ``verbose_name`` works."""
        self.assertEqual(self.mp._meta.get_field('body').verbose_name,
                         "post body")

    def test_markup_type_exclusive(self):
        """``MarkupMirrorField`` fields in models may only define one of
        ``markup_type`` or ``default_markup_type`` parameters.

        """
        self.assertRaises(ImproperlyConfigured,
                          MarkupMirrorField,
                          verbose_name='broken',
                          markup_type='markdown',
                          default_markup_type='markdown')

    def test_markup_body(self):
        """Tests the three accessors ``raw``, ``rendered`` and ``markup_type``
        of the ``Markup`` content wrapper class.

        """
        self.assertEquals(self.mp.body.raw, "**markdown**")
        self.assertEquals(self.mp.body.rendered,
                          "<p><strong>markdown</strong></p>")
        self.assertEquals(self.mp.body.markup_type, "markdown")

    def test_markup_unicode(self):
        """Converting ``Markup`` to unicode uses the ``Markup.rendered``
        accessor internally.

        """
        self.assertEqual(
            str(self.rp.body.rendered), textwrap.dedent(u"""\
            <div class="document">
            <p><em>reST</em></p>
            </div>
            """))

    def test_from_database(self):
        """Test that data loads back from the database correctly and 'post'
        has the right type.

        """
        post1 = Post.objects.get(pk=self.mp.pk)
        self.assertIsInstance(post1.body, Markup)
        self.assertEqual(str(post1.body.rendered),
                         "<p><strong>markdown</strong></p>")

    def test_pre_save(self):
        """Test that saving markup values for markup_types which are not
        registered fails.

        """
        post1 = Post.objects.get(pk=self.mp.pk)
        self.assertEqual(post1.body.markup_type, 'markdown')

        # unregister markdown
        del markup_pool['markdown']
        self.assertFalse('markdown' in markup_pool)

        # trying to save the Post now should fail
        self.assertRaises(ValueError, post1.save)

        # restore markup pool
        from markupmirror.markup import markdown_
        markup_pool.register_markup(markdown_.MarkdownMarkup)
        self.assertTrue('markdown' in markup_pool)

    def test_prepare_for_database(self):
        """Tests that ``Markup`` can be used to compare with
        ``MarkupMirrorField`` values at query time.

        """
        post1 = Post.objects.get(pk=self.mp.pk)
        body = post1.body
        self.assertIsInstance(body, Markup)
        self.assertEqual(post1, Post.objects.get(body=body))

    def test_accessor_classmethod(self):
        """``MarkupMirrorField`` attributes on model classes can only be
        accessed via an instance.

        """
        def access_body():
            return Post.body

        self.assertRaises(AttributeError, access_body)

    def test_accessor_none(self):
        """If the field is ``None`` that's what should be returned."""
        self.assertIsInstance(self.mp.body, Markup)
        self.mp.body = None
        self.assertEqual(self.mp.body, None)

    def test_body_assignment(self):
        """Setting the field's value works through the descriptor's setter
        (``MarkupMirrorFieldDescriptor.__set__``) and accepts both strings
        and ``Markup`` instances.

        """
        self.rp.body = "**reST**"
        self.rp.save()
        self.assertEquals(
            str(self.rp.body.rendered),
            textwrap.dedent(
                u"""\
                <div class="document">
                <p><strong>reST</strong></p>
                </div>
                """
            )
        )

        rest_markup = Markup(
            self.rp,
            'body',
            'body_rendered',
            'body_markup_type'
        )

        rest_markup.raw = "*reST*"
        self.rp.body = rest_markup
        self.rp.save()
        self.assertEquals(
            str(self.rp.body.rendered),
            textwrap.dedent(
                u"""\
                <div class="document">
                <p><em>reST</em></p>
                </div>
                """
            )
        )

    def test_raw_assignment(self):
        """Setting the ``Markup.raw`` property modifies the field's value."""

        self.rp.body.raw = '*more reST*'
        self.rp.save()
        self.assertEquals(
            str(self.rp.body.rendered),
            textwrap.dedent("""\
            <div class="document">
            <p><em>more reST</em></p>
            </div>
            """)
        )

    def test_rendered_assignment(self):
        """The ``Markup.rendered`` property dos not have a setter."""

        def set_rendered(text):
            self.rp.body.rendered = text

        self.assertRaises(AttributeError, set_rendered, "fail!")

    def test_body_type_assignment(self):
        """The markup type can be changed using the ``Markup.markup_type``
        property.

        """
        self.rp.body.markup_type = 'markdown'
        self.rp.save()
        self.assertEquals(self.rp.body.markup_type, 'markdown')
        self.assertEquals(str(self.rp.body.rendered),
                          "<p><em>reST</em></p>")

    def test_serialize_to_json(self):
        """Serializing a ``Post`` with a ``MarkupMirrorField`` works."""
        stream = serializers.serialize('json', Post.objects.all())
        json_data = json.loads(stream)
        self.assertEqual(json_data, [
            {
                'fields': {
                    'body': '**markdown**',
                    'body_markup_type': 'markdown',
                    'body_rendered': '<p><strong>markdown</strong></p>',
                    'comment': '',
                    'comment_markup_type': 'markdown',
                    'comment_rendered': '',
                    'markdown_field': '',
                    'markdown_field_markup_type': 'markdown',
                    'markdown_field_rendered': '',
                    'title': 'example Markdown post',
                },
                'model': 'tests.post',
                'pk': 1,
            },
            {
                'fields': {
                    'body': '*reST*',
                    'body_markup_type': 'restructuredtext',
                    'body_rendered': (
                        '<div class="document">'
                        '\n<p><em>reST</em></p>\n</div>\n'),
                    'comment': '',
                    'comment_markup_type': 'markdown',
                    'comment_rendered': '',
                    'markdown_field': '',
                    'markdown_field_markup_type': 'markdown',
                    'markdown_field_rendered': '',
                    'title': 'example reStructuredText post',
                },
                'model': 'tests.post',
                'pk': 2,
            },
            {
                'fields': {
                    'body': u"<script>alert('xss');</script>",
                    'body_markup_type': 'markdown',
                    'body_rendered': u"<script>alert('xss');</script>",
                    'comment': u"<script>alert('xss');</script>",
                    'comment_markup_type': 'markdown',
                    'comment_rendered': (
                        '<p>&lt;script&gt;'
                        'alert(&#39;xss&#39;);&lt;/script&gt;</p>'),
                    'markdown_field': '',
                    'markdown_field_markup_type': 'markdown',
                    'markdown_field_rendered': '',
                    'title': 'example XSS post',
                },
                'model': 'tests.post',
                'pk': 3,
            },
        ])

    def test_deserialize_json(self):
        """Tests that objects with ``MarkupMirrorFields`` can be deserialized
        correctly.

        """
        stream = serializers.serialize('json', Post.objects.all())
        obj = list(serializers.deserialize('json', stream))[0]
        self.assertEquals(obj.object, self.mp)

    def test_escape_html(self):
        """Rendered content should be escaped to prevent XSS attacks."""
        self.assertEquals(self.xss_post.comment.raw, self.xss_str)
        self.assertEquals(
            str(self.xss_post.comment.rendered),
            '<p>&lt;script&gt;alert(&#39;xss&#39;);&lt;/script&gt;</p>')

    def test_escape_html_false(self):
        """The ``MarkupMirrorField.escape_html`` prevents this escaping."""
        self.assertEquals(self.xss_post.body.raw, self.xss_str)
        self.assertEquals(str(self.xss_post.body.rendered), self.xss_str)

    def test_inheritance(self):
        """Abstract base models inherit the ``MarkupMirrorField`` to the
        concrete subclasses.

        """
        concrete_fields = [f.name for f in Concrete._meta.fields]
        self.assertEquals(
            concrete_fields,
            ['id', 'content', 'content_markup_type', 'content_rendered'])

    def test_markup_type_validation(self):
        """Invalid markup types are rejected."""
        self.assertRaises(
            ImproperlyConfigured, MarkupMirrorField,
            'verbose name', 'markup_field', 'bad_markup_type')

    def test_default_markup_types(self):
        """Per default the markup types plaintext, html are available.
        Depending on available third-party products, markdown,
        restructuredtext and textile are also in the markup pool.

        """
        markups = markup_pool.markups
        for markup_type, markup in markups.items():
            rendered = markup(u"test")
            self.assertTrue(hasattr(rendered, '__str__'))

    def test_formfield(self):
        """Form fields for ``MarkupMirrorFields`` always have two additional
        attributes:

        * ``class=markupmirror-editor``.
        * ``data-mm-settings``: a JSON dictionary containing the init settings
          for CodeMirror and the URLs and parameters required for the preview
          view.

        """
        form = PostForm()
        comment = form.fields['comment']
        self.assertEqual(
            sorted(comment.widget.attrs.keys()),
            ['class', 'cols', 'data-mm-settings', 'rows'])
        self.assertTrue('markupmirror-editor' in comment.widget.attrs['class'])
        self.assertEqual(
            comment.widget.attrs['data-mm-settings'],
            '{{"markup_type": "{0}", "mode": "{1}"}}'.format(
                self.mp.comment.markup_type,
                markup_pool[self.mp.comment.markup_type].codemirror_mode)
        )

from __future__ import absolute_import, unicode_literals

try:
    import json
except ImportError:
    import simplejson as json

import textwrap

from django.core import serializers
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase

from markupmirror.fields import Markup
from markupmirror.fields import MarkupMirrorField
from markupmirror.markup.base import markup_pool

from tests.models import Concrete, Post, PostForm

try:
    import json
except ImportError:
    import simplejson as json


__all__ = ('MarkupMirrorFieldTests',)


class MarkupMirrorFieldTests(TestCase):
    """Tests the ``markupmirror.fields.MarkupMirrorField`` implementation."""

    def setUp(self):
        """Creates three ``Post`` objects with different field settings."""

        # Post with Markdown field
        self.mp = Post(title="example Markdown post",
                       body="**markdown**", body_markup_type='markdown')
        self.mp.save()

        # Post with reStructuredText field
        self.rp = Post(title="example reStructuredText post",
                       body="*reST*", body_markup_type='restructuredtext')
        self.rp.save()

        # Post being attacked
        self.xss_str = "<script>alert('xss');</script>"
        self.xss_post = Post(title="example XSS post",
                             body=self.xss_str, body_markup_type='markdown',
                             comment=self.xss_str)
        self.xss_post.save()

    def test_verbose_name(self):
        """Tests that the standard field property ``verbose_name`` works."""
        self.assertEqual(self.mp._meta.get_field('body').verbose_name,
                         "post body")

    def test_markup_type_exclusive(self):
        """``MarkupMirrorField`` fields in models may only define one of
        ``markup_type`` or ``default_markup_type`` parameters.

        """
        self.assertRaises(ImproperlyConfigured,
                          MarkupMirrorField,
                          verbose_name='broken',
                          markup_type='markdown',
                          default_markup_type='markdown')

    def test_markup_body(self):
        """Tests the three accessors ``raw``, ``rendered`` and ``markup_type``
        of the ``Markup`` content wrapper class.

        """
        self.assertEquals(self.mp.body.raw, "**markdown**")
        self.assertEquals(self.mp.body.rendered,
                          "<p><strong>markdown</strong></p>")
        self.assertEquals(self.mp.body.markup_type, "markdown")

    def test_markup_unicode(self):
        """Converting ``Markup`` to unicode uses the ``Markup.rendered``
        accessor internally.

        """
        self.assertEqual(
            str(self.rp.body.rendered), textwrap.dedent(u"""\
            <div class="document">
            <p><em>reST</em></p>
            </div>
            """))

    def test_from_database(self):
        """Test that data loads back from the database correctly and 'post'
        has the right type.

        """
        post1 = Post.objects.get(pk=self.mp.pk)
        self.assertIsInstance(post1.body, Markup)
        self.assertEqual(str(post1.body.rendered),
                         "<p><strong>markdown</strong></p>")

    def test_pre_save(self):
        """Test that saving markup values for markup_types which are not
        registered fails.

        """
        post1 = Post.objects.get(pk=self.mp.pk)
        self.assertEqual(post1.body.markup_type, 'markdown')

        # unregister markdown
        del markup_pool['markdown']
        self.assertFalse('markdown' in markup_pool)

        # trying to save the Post now should fail
        self.assertRaises(ValueError, post1.save)

        # restore markup pool
        from markupmirror.markup import markdown_
        markup_pool.register_markup(markdown_.MarkdownMarkup)
        self.assertTrue('markdown' in markup_pool)

    def test_prepare_for_database(self):
        """Tests that ``Markup`` can be used to compare with
        ``MarkupMirrorField`` values at query time.

        """
        post1 = Post.objects.get(pk=self.mp.pk)
        body = post1.body
        self.assertIsInstance(body, Markup)
        self.assertEqual(post1, Post.objects.get(body=body))

    def test_accessor_classmethod(self):
        """``MarkupMirrorField`` attributes on model classes can only be
        accessed via an instance.

        """
        def access_body():
            return Post.body

        self.assertRaises(AttributeError, access_body)

    def test_accessor_none(self):
        """If the field is ``None`` that's what should be returned."""
        self.assertIsInstance(self.mp.body, Markup)
        self.mp.body = None
        self.assertEqual(self.mp.body, None)

    def test_body_assignment(self):
        """Setting the field's value works through the descriptor's setter
        (``MarkupMirrorFieldDescriptor.__set__``) and accepts both strings
        and ``Markup`` instances.

        """
        self.rp.body = "**reST**"
        self.rp.save()
        self.assertEquals(
            str(self.rp.body.rendered),
            textwrap.dedent(
                u"""\
                <div class="document">
                <p><strong>reST</strong></p>
                </div>
                """
            )
        )

        rest_markup = Markup(
            self.rp,
            'body',
            'body_rendered',
            'body_markup_type'
        )

        rest_markup.raw = "*reST*"
        self.rp.body = rest_markup
        self.rp.save()
        self.assertEquals(
            str(self.rp.body.rendered),
            textwrap.dedent(
                u"""\
                <div class="document">
                <p><em>reST</em></p>
                </div>
                """
            )
        )

    def test_raw_assignment(self):
        """Setting the ``Markup.raw`` property modifies the field's value."""

        self.rp.body.raw = '*more reST*'
        self.rp.save()
        self.assertEquals(
            str(self.rp.body.rendered),
            textwrap.dedent("""\
            <div class="document">
            <p><em>more reST</em></p>
            </div>
            """)
        )

    def test_rendered_assignment(self):
        """The ``Markup.rendered`` property dos not have a setter."""

        def set_rendered(text):
            self.rp.body.rendered = text

        self.assertRaises(AttributeError, set_rendered, "fail!")

    def test_body_type_assignment(self):
        """The markup type can be changed using the ``Markup.markup_type``
        property.

        """
        self.rp.body.markup_type = 'markdown'
        self.rp.save()
        self.assertEquals(self.rp.body.markup_type, 'markdown')
        self.assertEquals(str(self.rp.body.rendered),
                          "<p><em>reST</em></p>")

    def test_serialize_to_json(self):
        """Serializing a ``Post`` with a ``MarkupMirrorField`` works."""
        stream = serializers.serialize('json', Post.objects.all())
        json_data = json.loads(stream)
        self.assertEqual(json_data, [
            {
                'fields': {
                    'body': '**markdown**',
                    'body_markup_type': 'markdown',
                    'body_rendered': '<p><strong>markdown</strong></p>',
                    'comment': '',
                    'comment_markup_type': 'markdown',
                    'comment_rendered': '',
                    'markdown_field': '',
                    'markdown_field_markup_type': 'markdown',
                    'markdown_field_rendered': '',
                    'title': 'example Markdown post',
                },
                'model': 'tests.post',
                'pk': 1,
            },
            {
                'fields': {
                    'body': '*reST*',
                    'body_markup_type': 'restructuredtext',
                    'body_rendered': (
                        '<div class="document">'
                        '\n<p><em>reST</em></p>\n</div>\n'),
                    'comment': '',
                    'comment_markup_type': 'markdown',
                    'comment_rendered': '',
                    'markdown_field': '',
                    'markdown_field_markup_type': 'markdown',
                    'markdown_field_rendered': '',
                    'title': 'example reStructuredText post',
                },
                'model': 'tests.post',
                'pk': 2,
            },
            {
                'fields': {
                    'body': u"<script>alert('xss');</script>",
                    'body_markup_type': 'markdown',
                    'body_rendered': u"<script>alert('xss');</script>",
                    'comment': u"<script>alert('xss');</script>",
                    'comment_markup_type': 'markdown',
                    'comment_rendered': (
                        '<p>&lt;script&gt;'
                        'alert(&#39;xss&#39;);&lt;/script&gt;</p>'),
                    'markdown_field': '',
                    'markdown_field_markup_type': 'markdown',
                    'markdown_field_rendered': '',
                    'title': 'example XSS post',
                },
                'model': 'tests.post',
                'pk': 3,
            },
        ])

    def test_deserialize_json(self):
        """Tests that objects with ``MarkupMirrorFields`` can be deserialized
        correctly.

        """
        stream = serializers.serialize('json', Post.objects.all())
        obj = list(serializers.deserialize('json', stream))[0]
        self.assertEquals(obj.object, self.mp)

    def test_escape_html(self):
        """Rendered content should be escaped to prevent XSS attacks."""
        self.assertEquals(self.xss_post.comment.raw, self.xss_str)
        self.assertEquals(
            str(self.xss_post.comment.rendered),
            '<p>&lt;script&gt;alert(&#39;xss&#39;);&lt;/script&gt;</p>')

    def test_escape_html_false(self):
        """The ``MarkupMirrorField.escape_html`` prevents this escaping."""
        self.assertEquals(self.xss_post.body.raw, self.xss_str)
        self.assertEquals(str(self.xss_post.body.rendered), self.xss_str)

    def test_inheritance(self):
        """Abstract base models inherit the ``MarkupMirrorField`` to the
        concrete subclasses.

        """
        concrete_fields = [f.name for f in Concrete._meta.fields]
        self.assertEquals(
            concrete_fields,
            ['id', 'content', 'content_markup_type', 'content_rendered'])

    def test_markup_type_validation(self):
        """Invalid markup types are rejected."""
        self.assertRaises(
            ImproperlyConfigured, MarkupMirrorField,
            'verbose name', 'markup_field', 'bad_markup_type')

    def test_default_markup_types(self):
        """Per default the markup types plaintext, html are available.
        Depending on available third-party products, markdown,
        restructuredtext and textile are also in the markup pool.

        """
        markups = markup_pool.markups
        for markup_type, markup in markups.items():
            rendered = markup(u"test")
            self.assertTrue(hasattr(rendered, '__str__'))

    def test_formfield(self):
        """Form fields for ``MarkupMirrorFields`` always have two additional
        attributes:

        * ``class=markupmirror-editor``.
        * ``data-mm-settings``: a JSON dictionary containing the init settings
          for CodeMirror and the URLs and parameters required for the preview
          view.

        """
        form = PostForm()
        comment = form.fields['comment']
        self.assertEqual(
            sorted(comment.widget.attrs.keys()),
            ['class', 'cols', 'data-mm-settings', 'rows'])
        self.assertTrue('markupmirror-editor' in comment.widget.attrs['class'])
        self.assertEqual(
            comment.widget.attrs['data-mm-settings'],
            '{{"markup_type": "{0}", "mode": "{1}"}}'.format(
                self.mp.comment.markup_type,
                markup_pool[self.mp.comment.markup_type].codemirror_mode)
        )
Tests the ``markupmirror.fields.MarkupMirrorField`` implementation. Creates three ``Post`` objects with different field settings. # Post with Markdown field # Post with reStructuredText field # Post being attacked Tests that the standard field property ``verbose_name`` works. ``MarkupMirrorField`` fields in models may only define one of         ``markup_type`` or ``default_markup_type`` parameters. Tests the three accessors ``raw``, ``rendered`` and ``markup_type``         of the ``Markup`` content wrapper class. Converting ``Markup`` to unicode uses the ``Markup.rendered``         accessor internally. \             <div class="document">             <p><em>reST</em></p>             </div> Test that data loads back from the database correctly and 'post'         has the right type. Test that saving markup values for markup_types which are not         registered fails. # unregister markdown # trying to save the Post now should fail # restore markup pool Tests that ``Markup`` can be used to compare with         ``MarkupMirrorField`` values at query time. ``MarkupMirrorField`` attributes on model classes can only be         accessed via an instance. If the field is ``None`` that's what should be returned. Setting the field's value works through the descriptor's setter         (``MarkupMirrorFieldDescriptor.__set__``) and accepts both strings         and ``Markup`` instances. \                 <div class="document">                 <p><strong>reST</strong></p>                 </div> \                 <div class="document">                 <p><em>reST</em></p>                 </div> Setting the ``Markup.raw`` property modifies the field's value. \             <div class="document">             <p><em>more reST</em></p>             </div> The ``Markup.rendered`` property dos not have a setter. The markup type can be changed using the ``Markup.markup_type``         property. Serializing a ``Post`` with a ``MarkupMirrorField`` works. #39;xss&#39;);&lt;/script&gt;</p>'), Tests that objects with ``MarkupMirrorFields`` can be deserialized         correctly. Rendered content should be escaped to prevent XSS attacks. #39;xss&#39;);&lt;/script&gt;</p>') The ``MarkupMirrorField.escape_html`` prevents this escaping. Abstract base models inherit the ``MarkupMirrorField`` to the         concrete subclasses. Invalid markup types are rejected. Per default the markup types plaintext, html are available.         Depending on available third-party products, markdown,         restructuredtext and textile are also in the markup pool. Form fields for ``MarkupMirrorFields`` always have two additional         attributes:          * ``class=markupmirror-editor``.         * ``data-mm-settings``: a JSON dictionary containing the init settings           for CodeMirror and the URLs and parameters required for the preview           view.