Source

django-selectable / selectable / tests / base.py

Full commit
import random
import string
from xml.dom.minidom import parseString

from django.conf import settings
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.utils.html import escape
from django.utils.safestring import SafeData, mark_safe

from selectable.base import ModelLookup
from selectable.tests import Thing

__all__ = (
    'ModelLookupTestCase',
    'MultiFieldLookupTestCase',
    'LookupEscapingTestCase',
)


def as_xml(html):
    "Convert HTML portion to minidom node."
    return parseString('<root>%s</root>' % html)


def parsed_inputs(html):
    "Returns a dictionary mapping name --> node of inputs found in the HTML."
    node = as_xml(html)
    inputs = {}
    for field in node.getElementsByTagName('input'):
        name = dict(field.attributes.items())['name']
        current = inputs.get(name, [])
        current.append(field)
        inputs[name] = current
    return inputs


class PatchSettingsMixin(object):
    def setUp(self):
        super(PatchSettingsMixin, self).setUp()
        self.is_limit_set = hasattr(settings, 'SELECTABLE_MAX_LIMIT')
        if self.is_limit_set:
            self.original_limit = settings.SELECTABLE_MAX_LIMIT
        settings.SELECTABLE_MAX_LIMIT = 25

    def tearDown(self):
        super(PatchSettingsMixin, self).tearDown()
        if self.is_limit_set:
            settings.SELECTABLE_MAX_LIMIT = self.original_limit


class BaseSelectableTestCase(TestCase):
    urls = 'selectable.tests.urls'

    def get_random_string(self, length=10):
        return u''.join(random.choice(string.ascii_letters) for x in xrange(length))

    def create_thing(self, data=None):
        data = data or {}
        defaults = {
            'name': self.get_random_string(),
            'description': self.get_random_string(),
        }
        defaults.update(data)
        return Thing.objects.create(**defaults)


class SimpleModelLookup(ModelLookup):
    model = Thing
    search_fields = ('name__icontains', )


class ModelLookupTestCase(BaseSelectableTestCase):
    lookup_cls = SimpleModelLookup

    def get_lookup_instance(self):
        return self.__class__.lookup_cls()

    def test_get_name(self):
        name = self.__class__.lookup_cls.name()
        self.assertEqual(name, 'tests-simplemodellookup')

    def test_get_url(self):
        url = self.__class__.lookup_cls.url()
        test_url = reverse('selectable-lookup', args=['tests-simplemodellookup'])
        self.assertEqual(url, test_url)

    def test_format_item(self):
        lookup = self.get_lookup_instance()
        thing = Thing()
        item_info = lookup.format_item(thing)
        self.assertTrue('id' in item_info)
        self.assertTrue('value' in item_info)
        self.assertTrue('label' in item_info)

    def test_get_query(self):
        lookup = self.get_lookup_instance()
        thing = self.create_thing(data={'name': 'Thing'})
        other_thing = self.create_thing(data={'name': 'Other Thing'})
        qs = lookup.get_query(request=None, term='other')
        self.assertTrue(thing.pk not in qs.values_list('id', flat=True))
        self.assertTrue(other_thing.pk in qs.values_list('id', flat=True))

    def test_create_item(self):
        value = self.get_random_string()
        lookup = self.get_lookup_instance()
        thing = lookup.create_item(value)
        self.assertEqual(thing.__class__, Thing)
        self.assertEqual(thing.name, value)
        self.assertFalse(thing.pk)

    def test_get_item(self):
        lookup = self.get_lookup_instance()
        thing = self.create_thing(data={'name': 'Thing'})
        item = lookup.get_item(thing.pk)
        self.assertEqual(thing, item)

    def test_format_item_escaping(self):
        "Id, value and label should be escaped."
        lookup = self.get_lookup_instance()
        thing = self.create_thing(data={'name': 'Thing'})
        item_info = lookup.format_item(thing)
        self.assertFalse(isinstance(item_info['id'], SafeData))
        self.assertFalse(isinstance(item_info['value'], SafeData))
        self.assertTrue(isinstance(item_info['label'], SafeData))


class MultiFieldLookup(ModelLookup):
    model = Thing
    search_fields = ('name__icontains', 'description__icontains', )


class MultiFieldLookupTestCase(ModelLookupTestCase):
    lookup_cls = MultiFieldLookup

    def test_get_name(self):
        name = self.__class__.lookup_cls.name()
        self.assertEqual(name, 'tests-multifieldlookup')

    def test_get_url(self):
        url = self.__class__.lookup_cls.url()
        test_url = reverse('selectable-lookup', args=['tests-multifieldlookup'])
        self.assertEqual(url, test_url)

    def test_description_search(self):
        lookup = self.get_lookup_instance()
        thing = self.create_thing(data={'description': 'Thing'})
        other_thing = self.create_thing(data={'description': 'Other Thing'})
        qs = lookup.get_query(request=None, term='other')
        self.assertTrue(thing.pk not in qs.values_list('id', flat=True))
        self.assertTrue(other_thing.pk in qs.values_list('id', flat=True))


class HTMLLookup(ModelLookup):
    model = Thing
    search_fields = ('name__icontains', )


class SafeHTMLLookup(ModelLookup):
    model = Thing
    search_fields = ('name__icontains', )

    def get_item_label(self, item):
        "Mark label as safe."
        return mark_safe(item.name)


class LookupEscapingTestCase(BaseSelectableTestCase):

    def test_escape_html(self):
        "HTML should be escaped by default."
        lookup = HTMLLookup()
        bad_name = "<script>alert('hacked');</script>"
        escaped_name = escape(bad_name)
        thing = self.create_thing(data={'name': bad_name})
        item_info = lookup.format_item(thing)
        self.assertEqual(item_info['label'], escaped_name)

    def test_conditional_escape(self):
        "Methods should be able to mark values as safe."
        lookup = SafeHTMLLookup()
        bad_name = "<script>alert('hacked');</script>"
        escaped_name = escape(bad_name)
        thing = self.create_thing(data={'name': bad_name})
        item_info = lookup.format_item(thing)
        self.assertEqual(item_info['label'], bad_name)