From dc61e30ab8329093150a1eb9333b1409986a07c6 Mon Sep 17 00:00:00 2001 From: Magnus Hagander Date: Wed, 6 Jan 2016 16:27:59 +0100 Subject: [PATCH] Import a new version of django-selectable Needed for django 1.8 --- dep/django-selectable/AUTHORS.txt | 6 + dep/django-selectable/LICENSE.txt | 2 +- dep/django-selectable/PKG-INFO | 92 +++ dep/django-selectable/README.rst | 17 +- dep/django-selectable/selectable/__init__.py | 4 +- dep/django-selectable/selectable/apps.py | 14 + dep/django-selectable/selectable/base.py | 5 +- dep/django-selectable/selectable/compat.py | 18 +- .../selectable/forms/base.py | 3 +- .../selectable/forms/fields.py | 31 +- .../selectable/forms/widgets.py | 160 +++--- .../locale/en/LC_MESSAGES/django.mo | Bin 378 -> 378 bytes .../locale/en/LC_MESSAGES/django.po | 4 +- .../locale/es/LC_MESSAGES/django.mo | Bin 714 -> 721 bytes .../locale/es/LC_MESSAGES/django.po | 20 +- .../locale/fr/LC_MESSAGES/django.mo | Bin 0 -> 725 bytes .../locale/fr/LC_MESSAGES/django.po | 28 + .../locale/pl/LC_MESSAGES/django.mo | Bin 761 -> 761 bytes .../locale/pl/LC_MESSAGES/django.po | 23 +- .../locale/pt_BR/LC_MESSAGES/django.mo | Bin 658 -> 733 bytes .../locale/pt_BR/LC_MESSAGES/django.po | 19 +- .../locale/zh_CN/LC_MESSAGES/django.mo | Bin 0 -> 709 bytes .../locale/zh_CN/LC_MESSAGES/django.po | 28 + dep/django-selectable/selectable/registry.py | 33 +- .../static/selectable/css/dj.selectable.css | 4 +- .../selectable/js/jquery.dj.selectable.js | 35 +- .../selectable/tests/__init__.py | 30 +- .../selectable/tests/base.py | 131 +---- .../selectable/tests/test_base.py | 131 +++++ .../selectable/tests/test_decorators.py | 94 +++ .../selectable/tests/test_fields.py | 134 +++++ .../selectable/tests/test_forms.py | 86 +++ .../selectable/tests/test_functional.py | 538 ++++++++++++++++++ .../selectable/tests/test_templatetags.py | 115 ++++ .../selectable/tests/test_views.py | 91 +++ .../selectable/tests/test_widgets.py | 477 ++++++++++++++++ .../selectable/tests/urls.py | 13 +- .../selectable/tests/views.py | 92 --- dep/django-selectable/selectable/urls.py | 21 +- dep/django-selectable/setup.cfg | 8 + dep/django-selectable/setup.py | 3 +- 41 files changed, 2084 insertions(+), 426 deletions(-) create mode 100644 dep/django-selectable/PKG-INFO create mode 100644 dep/django-selectable/selectable/apps.py create mode 100644 dep/django-selectable/selectable/locale/fr/LC_MESSAGES/django.mo create mode 100644 dep/django-selectable/selectable/locale/fr/LC_MESSAGES/django.po create mode 100644 dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.mo create mode 100644 dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.po create mode 100644 dep/django-selectable/selectable/tests/test_base.py create mode 100644 dep/django-selectable/selectable/tests/test_decorators.py create mode 100644 dep/django-selectable/selectable/tests/test_fields.py create mode 100644 dep/django-selectable/selectable/tests/test_forms.py create mode 100644 dep/django-selectable/selectable/tests/test_functional.py create mode 100644 dep/django-selectable/selectable/tests/test_templatetags.py create mode 100644 dep/django-selectable/selectable/tests/test_views.py create mode 100644 dep/django-selectable/selectable/tests/test_widgets.py create mode 100644 dep/django-selectable/setup.cfg diff --git a/dep/django-selectable/AUTHORS.txt b/dep/django-selectable/AUTHORS.txt index 3c1e4d2..1c0422f 100644 --- a/dep/django-selectable/AUTHORS.txt +++ b/dep/django-selectable/AUTHORS.txt @@ -17,5 +17,11 @@ Rick Testore Karen Tracey Manuel Alvarez Ustun Ozgur +@leo-the-manic +Calvin Spealman +Rebecca Lovewell +Thomas Güttler +Yuri Khrustalev +@SaeX Thanks for all of your work! diff --git a/dep/django-selectable/LICENSE.txt b/dep/django-selectable/LICENSE.txt index a208c39..c50fbf0 100644 --- a/dep/django-selectable/LICENSE.txt +++ b/dep/django-selectable/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2010-2013, Mark Lavin +Copyright (c) 2010-2014, Mark Lavin All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/dep/django-selectable/PKG-INFO b/dep/django-selectable/PKG-INFO new file mode 100644 index 0000000..4a7072d --- /dev/null +++ b/dep/django-selectable/PKG-INFO @@ -0,0 +1,92 @@ +Metadata-Version: 1.1 +Name: django-selectable +Version: 0.9.0 +Summary: Auto-complete selection widgets using Django and jQuery UI. +Home-page: https://github.com/mlavin/django-selectable +Author: Mark Lavin +Author-email: markdlavin@gmail.com +License: BSD +Description: django-selectable + =================== + + Tools and widgets for using/creating auto-complete selection widgets using Django and jQuery UI. + + .. image:: https://travis-ci.org/mlavin/django-selectable.svg?branch=master + :target: https://travis-ci.org/mlavin/django-selectable + + + Features + ----------------------------------- + + - Works with the latest jQuery UI Autocomplete library + - Auto-discovery/registration pattern for defining lookups + + + Installation Requirements + ----------------------------------- + + - Python 2.6-2.7, 3.2+ + - `Django `_ >= 1.5 + - `jQuery `_ >= 1.7 + - `jQuery UI `_ >= 1.8 + + To install:: + + pip install django-selectable + + Next add `selectable` to your `INSTALLED_APPS` to include the related css/js:: + + INSTALLED_APPS = ( + 'contrib.staticfiles', + # Other apps here + 'selectable', + ) + + The jQuery and jQuery UI libraries are not included in the distribution but must be included + in your templates. See the example project for an example using these libraries from the + Google CDN. + + Once installed you should add the urls to your root url patterns:: + + urlpatterns = patterns('', + # Other patterns go here + (r'^selectable/', include('selectable.urls')), + ) + + + Documentation + ----------------------------------- + + Documentation for django-selectable is available on `Read The Docs `_. + + + Additional Help/Support + ----------------------------------- + + You can find additional help or support on the mailing list: http://groups.google.com/group/django-selectable + + + Contributing + -------------------------------------- + + If you think you've found a bug or are interested in contributing to this project + check out our `contributing guide `_. + + If you are interested in translating django-selectable into your native language + you can join the `Transifex project `_. + + +Platform: UNKNOWN +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Framework :: Django +Classifier: Development Status :: 5 - Production/Stable +Classifier: Operating System :: OS Independent diff --git a/dep/django-selectable/README.rst b/dep/django-selectable/README.rst index 6930524..0219c2c 100644 --- a/dep/django-selectable/README.rst +++ b/dep/django-selectable/README.rst @@ -3,6 +3,10 @@ django-selectable Tools and widgets for using/creating auto-complete selection widgets using Django and jQuery UI. +.. image:: https://travis-ci.org/mlavin/django-selectable.svg?branch=master + :target: https://travis-ci.org/mlavin/django-selectable + + Features ----------------------------------- @@ -13,18 +17,11 @@ Features Installation Requirements ----------------------------------- -- Python 2.6 or Python 2.7 -- `Django `_ >= 1.3 -- `jQuery `_ >= 1.4.4 +- Python 2.6-2.7, 3.2+ +- `Django `_ >= 1.5 +- `jQuery `_ >= 1.7 - `jQuery UI `_ >= 1.8 -.. note:: - - Begining with version django-selectable version 0.6, Django 1.2 is no longer supported. - While it may continue to work, bugs related to Django 1.2 support will not be fixed. - - Version 0.7 adds experimental support for Python 3.2+ when used with Django 1.5+. - To install:: pip install django-selectable diff --git a/dep/django-selectable/selectable/__init__.py b/dep/django-selectable/selectable/__init__.py index 7c38b67..b4232fc 100644 --- a/dep/django-selectable/selectable/__init__.py +++ b/dep/django-selectable/selectable/__init__.py @@ -1,4 +1,6 @@ "Auto-complete selection widgets using Django and jQuery UI." -__version__ = '0.7.0' \ No newline at end of file +__version__ = '0.9.0' + +default_app_config = 'selectable.apps.SelectableConfig' diff --git a/dep/django-selectable/selectable/apps.py b/dep/django-selectable/selectable/apps.py new file mode 100644 index 0000000..f65c5e1 --- /dev/null +++ b/dep/django-selectable/selectable/apps.py @@ -0,0 +1,14 @@ +try: + from django.apps import AppConfig +except ImportError: + AppConfig = object + + +class SelectableConfig(AppConfig): + """App configuration for django-selectable.""" + + name = 'selectable' + + def ready(self): + from . import registry + registry.autodiscover() diff --git a/dep/django-selectable/selectable/base.py b/dep/django-selectable/selectable/base.py index fe7c5ee..8335ddd 100644 --- a/dep/django-selectable/selectable/base.py +++ b/dep/django-selectable/selectable/base.py @@ -146,7 +146,10 @@ class ModelLookup(LookupBase): return qs def get_queryset(self): - qs = self.model._default_manager.get_query_set() + try: + qs = self.model._default_manager.get_queryset() + except AttributeError: # Django <= 1.5. + qs = self.model._default_manager.get_query_set() if self.filters: qs = qs.filter(**self.filters) return qs diff --git a/dep/django-selectable/selectable/compat.py b/dep/django-selectable/selectable/compat.py index 7356db1..a63ab6a 100644 --- a/dep/django-selectable/selectable/compat.py +++ b/dep/django-selectable/selectable/compat.py @@ -12,9 +12,25 @@ except ImportError: from django.utils.encoding import smart_unicode as smart_text from django.utils.encoding import force_unicode as force_text +try: + from django.forms.utils import flatatt +except ImportError: + from django.forms.util import flatatt + PY3 = sys.version_info[0] == 3 if PY3: string_types = str, else: - string_types = basestring, \ No newline at end of file + string_types = basestring, + +try: + from importlib import import_module +except ImportError: + from django.utils.importlib import import_module + +try: + from django.apps import AppConfig + LEGACY_AUTO_DISCOVER = False +except ImportError: + LEGACY_AUTO_DISCOVER = True diff --git a/dep/django-selectable/selectable/forms/base.py b/dep/django-selectable/selectable/forms/base.py index 21ee889..c39c2f7 100644 --- a/dep/django-selectable/selectable/forms/base.py +++ b/dep/django-selectable/selectable/forms/base.py @@ -2,9 +2,8 @@ from __future__ import unicode_literals from django import forms from django.conf import settings -from django.utils.importlib import import_module -from selectable.compat import string_types +from selectable.compat import string_types, import_module __all__ = ( diff --git a/dep/django-selectable/selectable/forms/fields.py b/dep/django-selectable/selectable/forms/fields.py index f6004cc..21109c4 100644 --- a/dep/django-selectable/selectable/forms/fields.py +++ b/dep/django-selectable/selectable/forms/fields.py @@ -4,6 +4,7 @@ from django import forms from django.core.exceptions import ValidationError from django.core.validators import EMPTY_VALUES from django.utils.translation import ugettext_lazy as _ +from django.db.models import Model from selectable.forms.base import import_lookup_class from selectable.forms.widgets import AutoCompleteSelectWidget @@ -15,7 +16,32 @@ __all__ = ( ) -class AutoCompleteSelectField(forms.Field): +def model_vars(obj): + fields = dict( + (field.name, getattr(obj, field.name)) + for field in obj._meta.fields + ) + return fields + + +class BaseAutoCompleteField(forms.Field): + + def _has_changed(self, initial, data): + "Detects if the data was changed. This is added in 1.6." + if initial is None and data is None: + return False + if data and not hasattr(data, '__iter__'): + data = self.widget.decompress(data) + initial = self.to_python(initial) + data = self.to_python(data) + if hasattr(self, '_coerce'): + data = self._coerce(data) + if isinstance(data, Model) and isinstance(initial, Model): + return model_vars(data) != model_vars(initial) + else: + return data != initial + +class AutoCompleteSelectField(BaseAutoCompleteField): widget = AutoCompleteSelectWidget default_error_messages = { @@ -32,6 +58,7 @@ class AutoCompleteSelectField(forms.Field): super(AutoCompleteSelectField, self).__init__(*args, **kwargs) + def to_python(self, value): if value in EMPTY_VALUES: return None @@ -62,7 +89,7 @@ class AutoCompleteSelectField(forms.Field): return value -class AutoCompleteSelectMultipleField(forms.Field): +class AutoCompleteSelectMultipleField(BaseAutoCompleteField): widget = AutoCompleteSelectMultipleWidget default_error_messages = { diff --git a/dep/django-selectable/selectable/forms/widgets.py b/dep/django-selectable/selectable/forms/widgets.py index f0d3087..f23332c 100644 --- a/dep/django-selectable/selectable/forms/widgets.py +++ b/dep/django-selectable/selectable/forms/widgets.py @@ -2,14 +2,13 @@ from __future__ import unicode_literals import json -from django import forms +from django import forms, VERSION as DJANGO_VERSION from django.conf import settings -from django.forms.util import flatatt from django.utils.http import urlencode from django.utils.safestring import mark_safe from selectable import __version__ -from selectable.compat import force_text +from selectable.compat import force_text, flatatt from selectable.forms.base import import_lookup_class __all__ = ( @@ -33,6 +32,7 @@ class SelectableMediaMixin(object): } js = ('%sjs/jquery.dj.selectable.js?v=%s' % (STATIC_PREFIX, __version__),) + class AutoCompleteWidget(forms.TextInput, SelectableMediaMixin): def __init__(self, lookup_class, *args, **kwargs): @@ -65,13 +65,14 @@ class SelectableMultiWidget(forms.MultiWidget): def update_query_parameters(self, qs_dict): self.widgets[0].update_query_parameters(qs_dict) - def _has_changed(self, initial, data): - "Decects if the widget was changed. This is removed in 1.6." - if initial is None and data is None: - return False - if data and not hasattr(data, '__iter__'): - data = self.decompress(data) - return super(SelectableMultiWidget, self)._has_changed(initial, data) + if DJANGO_VERSION < (1, 6): + def _has_changed(self, initial, data): + "Detects if the widget was changed. This is removed in Django 1.6." + if initial is None and data is None: + return False + if data and not hasattr(data, '__iter__'): + data = self.decompress(data) + return super(SelectableMultiWidget, self)._has_changed(initial, data) def decompress(self, value): if value: @@ -86,8 +87,29 @@ class SelectableMultiWidget(forms.MultiWidget): return [item_value, value] return [None, None] + def get_compatible_postdata(self, data, name): + """Get postdata built for a normal ``-compatibile post variable + is not found. + """ + return data.get(name, None) -class AutoCompleteSelectWidget(SelectableMultiWidget, SelectableMediaMixin): + +class _BaseSingleSelectWidget(SelectableMultiWidget, SelectableMediaMixin): + """ + Common base class for AutoCompleteSelectWidget and AutoComboboxSelectWidget + each which use one widget (primary_widget) to select text and a single + hidden input to hold the selected id. + """ + + primary_widget = None def __init__(self, lookup_class, *args, **kwargs): self.lookup_class = import_lookup_class(lookup_class) @@ -95,22 +117,31 @@ class AutoCompleteSelectWidget(SelectableMultiWidget, SelectableMediaMixin): self.limit = kwargs.pop('limit', None) query_params = kwargs.pop('query_params', {}) widgets = [ - AutoCompleteWidget( + self.primary_widget( lookup_class, allow_new=self.allow_new, limit=self.limit, query_params=query_params, attrs=kwargs.get('attrs'), ), forms.HiddenInput(attrs={'data-selectable-type': 'hidden'}) ] - super(AutoCompleteSelectWidget, self).__init__(widgets, *args, **kwargs) + super(_BaseSingleSelectWidget, self).__init__(widgets, *args, **kwargs) def value_from_datadict(self, data, files, name): - value = super(AutoCompleteSelectWidget, self).value_from_datadict(data, files, name) + value = super(_BaseSingleSelectWidget, self).value_from_datadict(data, files, name) + if not value[1]: + compatible_postdata = self.get_compatible_postdata(data, name) + if compatible_postdata: + value[1] = compatible_postdata if not self.allow_new: return value[1] return value +class AutoCompleteSelectWidget(_BaseSingleSelectWidget): + + primary_widget = AutoCompleteWidget + + class AutoComboboxWidget(AutoCompleteWidget, SelectableMediaMixin): def build_attrs(self, extra_attrs=None, **kwargs): @@ -119,28 +150,9 @@ class AutoComboboxWidget(AutoCompleteWidget, SelectableMediaMixin): return attrs -class AutoComboboxSelectWidget(SelectableMultiWidget, SelectableMediaMixin): +class AutoComboboxSelectWidget(_BaseSingleSelectWidget): - def __init__(self, lookup_class, *args, **kwargs): - self.lookup_class = import_lookup_class(lookup_class) - self.allow_new = kwargs.pop('allow_new', False) - self.limit = kwargs.pop('limit', None) - query_params = kwargs.pop('query_params', {}) - widgets = [ - AutoComboboxWidget( - lookup_class, allow_new=self.allow_new, - limit=self.limit, query_params=query_params, - attrs=kwargs.get('attrs'), - ), - forms.HiddenInput(attrs={'data-selectable-type': 'hidden'}) - ] - super(AutoComboboxSelectWidget, self).__init__(widgets, *args, **kwargs) - - def value_from_datadict(self, data, files, name): - value = super(AutoComboboxSelectWidget, self).value_from_datadict(data, files, name) - if not self.allow_new: - return value[1] - return value + primary_widget = AutoComboboxWidget class LookupMultipleHiddenInput(forms.MultipleHiddenInput): @@ -178,7 +190,14 @@ class LookupMultipleHiddenInput(forms.MultipleHiddenInput): return attrs -class AutoCompleteSelectMultipleWidget(SelectableMultiWidget, SelectableMediaMixin): +class _BaseMultipleSelectWidget(SelectableMultiWidget, SelectableMediaMixin): + """ + Common base class for AutoCompleteSelectMultipleWidget and AutoComboboxSelectMultipleWidget + each which use one widget (primary_widget) to select text and a multiple + hidden inputs to hold the selected ids. + """ + + primary_widget = None def __init__(self, lookup_class, *args, **kwargs): self.lookup_class = import_lookup_class(lookup_class) @@ -191,71 +210,44 @@ class AutoCompleteSelectMultipleWidget(SelectableMultiWidget, SelectableMediaMix attrs.update(kwargs.get('attrs', {})) query_params = kwargs.pop('query_params', {}) widgets = [ - AutoCompleteWidget( + self.primary_widget( lookup_class, allow_new=False, limit=self.limit, query_params=query_params, attrs=attrs ), LookupMultipleHiddenInput(lookup_class) ] - super(AutoCompleteSelectMultipleWidget, self).__init__(widgets, *args, **kwargs) + super(_BaseMultipleSelectWidget, self).__init__(widgets, *args, **kwargs) def value_from_datadict(self, data, files, name): - return self.widgets[1].value_from_datadict(data, files, name + '_1') + value = self.widgets[1].value_from_datadict(data, files, name + '_1') + if not value: + # Fall back to the compatible POST name + value = self.get_compatible_postdata(data, name) + return value def render(self, name, value, attrs=None): if value and not hasattr(value, '__iter__'): value = [value] value = ['', value] - return super(AutoCompleteSelectMultipleWidget, self).render(name, value, attrs) - - def _has_changed(self, initial, data): - """" - Decects if the widget was changed. This is removed in 1.6. - - For the multi-select case we only care if the hidden inputs changed. - """ - initial = ['', initial] - data = ['', data] - return super(AutoCompleteSelectMultipleWidget, self)._has_changed(initial, data) + return super(_BaseMultipleSelectWidget, self).render(name, value, attrs) + if DJANGO_VERSION < (1, 6): + def _has_changed(self, initial, data): + """" + Detects if the widget was changed. This is removed in Django 1.6. + For the multi-select case we only care if the hidden inputs changed. + """ + initial = ['', initial] + data = ['', data] + return super(_BaseMultipleSelectWidget, self)._has_changed(initial, data) -class AutoComboboxSelectMultipleWidget(SelectableMultiWidget, SelectableMediaMixin): - def __init__(self, lookup_class, *args, **kwargs): - self.lookup_class = import_lookup_class(lookup_class) - self.limit = kwargs.pop('limit', None) - position = kwargs.pop('position', 'bottom') - attrs = { - 'data-selectable-multiple': 'true', - 'data-selectable-position': position - } - attrs.update(kwargs.get('attrs', {})) - query_params = kwargs.pop('query_params', {}) - widgets = [ - AutoComboboxWidget( - lookup_class, allow_new=False, - limit=self.limit, query_params=query_params, attrs=attrs - ), - LookupMultipleHiddenInput(lookup_class) - ] - super(AutoComboboxSelectMultipleWidget, self).__init__(widgets, *args, **kwargs) +class AutoCompleteSelectMultipleWidget(_BaseMultipleSelectWidget): - def value_from_datadict(self, data, files, name): - return self.widgets[1].value_from_datadict(data, files, name + '_1') + primary_widget = AutoCompleteWidget - def render(self, name, value, attrs=None): - if value and not hasattr(value, '__iter__'): - value = [value] - value = ['', value] - return super(AutoComboboxSelectMultipleWidget, self).render(name, value, attrs) - def _has_changed(self, initial, data): - """" - Decects if the widget was changed. This is removed in 1.6. +class AutoComboboxSelectMultipleWidget(_BaseMultipleSelectWidget): - For the multi-select case we only care if the hidden inputs changed. - """ - initial = ['', initial] - data = ['', data] - return super(AutoComboboxSelectMultipleWidget, self)._has_changed(initial, data) + primary_widget = AutoComboboxWidget diff --git a/dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.mo b/dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.mo index bd87d37359effc604c08909beda7721bcc777bc8..99d42adf6d4725d6cd040d73b3421b4c81cd49d5 100644 GIT binary patch delta 26 hcmeyx^owaimw<_`p@FWEp@NZtm7$5Qfyu;K4*+m{2r>Ww delta 26 hcmeyx^owaimw>UZfswAExq_jIm7#^Mf$79q4*+oW2tNP- diff --git a/dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.po b/dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.po index 942993a..0f1f178 100644 --- a/dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.po +++ b/dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-02-17 14:18-0500\n" +"POT-Creation-Date: 2014-10-21 20:14-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -21,6 +21,6 @@ msgstr "" msgid "Show more results" msgstr "" -#: forms/fields.py:22 forms/fields.py:69 +#: forms/fields.py:48 forms/fields.py:96 msgid "Select a valid choice. That choice is not one of the available choices." msgstr "" diff --git a/dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.mo b/dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.mo index d08ce3531941458c7e872b3e6f42d5bbd50eea7a..96ac6a1de827b81edec48f96f29e7474a7b5174a 100644 GIT binary patch delta 126 zcmX@bdXaU4iRcDK28P`X3=BR%oWsPxAP%H=PV_t~V5Dnkple{JU}$P(V5DncGFgsM z%D~W2*T_J@(7?*jLfZfcxO@_eOLRku67!045=-)ntQ33`^GZ{56dZHP5{pu+CMPl$ aPQK3=#p9EhmtLBfo@%9#T0Gf;X%Ya44I;n* delta 119 zcmcb}dWv;|iRem328P`X3=BR%oXW(&AP%HAP4qk}V61Ckq-$udU}$1xXrXIhI$4fU zN)sex3=%Rj)HVPDE}z8W65WuZ#Ju91#FG3XD}~hJ7#K{?qg*-lqdFiEz>8T*m N$u}6ICp$1r1OSy`AWZ-O diff --git a/dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.po b/dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.po index 438d095..6a6bc04 100644 --- a/dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.po +++ b/dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.po @@ -1,27 +1,27 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: msgid "" msgstr "" "Project-Id-Version: django-selectable\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-02-17 14:18-0500\n" -"PO-Revision-Date: 2013-02-13 14:21+0000\n" -"Last-Translator: escriva \n" -"Language-Team: Spanish (http://www.transifex.com/projects/p/django-" -"selectable/language/es/)\n" -"Language: es\n" +"POT-Creation-Date: 2012-10-06 15:02-0400\n" +"PO-Revision-Date: 2013-11-20 10:18+0000\n" +"Last-Translator: Manuel Alvarez \n" +"Language-Team: Spanish (http://www.transifex.com/projects/p/django-selectable/language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: base.py:117 +#: base.py:115 msgid "Show more results" msgstr "Mostrar más resultados" -#: forms/fields.py:22 forms/fields.py:69 -msgid "Select a valid choice. That choice is not one of the available choices." +#: forms/fields.py:19 forms/fields.py:63 +msgid "" +"Select a valid choice. That choice is not one of the available choices." msgstr "Seleccione una opción válida. La opción seleccionada no está disponible." diff --git a/dep/django-selectable/selectable/locale/fr/LC_MESSAGES/django.mo b/dep/django-selectable/selectable/locale/fr/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..651efbbe29af5ff1e68b9f09975e9912e96e06d6 GIT binary patch literal 725 zcmZWn&5qMB5DtHr+~5j_IqnL%ajJq=DO~|&i&kp2)i!WuHkl+|5<9Y;w(uT2L=QXw z;<)d@voL8Z_P|J=W^B)oXXg9!?D3C)j{@!mSWGsd;$T*Sm zrDO@`=)6cRYFDE-Hh~4&uQ128((tBXJFk2DZP{>ZgUmGN9HPq(OA;wzlfcZ9&MG*m zyl;~z+H5uh-{(^WZTo!Y?trN=yCw2u7bQ#uo8_kMlpf~>gEnaPzd6F9q3{@, 2014 +msgid "" +msgstr "" +"Project-Id-Version: django-selectable\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-10-06 15:02-0400\n" +"PO-Revision-Date: 2014-01-21 01:00+0000\n" +"Last-Translator: Mark Lavin \n" +"Language-Team: French (http://www.transifex.com/projects/p/django-selectable/language/fr/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: fr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: base.py:115 +msgid "Show more results" +msgstr "Afficher plus de résultats" + +#: forms/fields.py:19 forms/fields.py:63 +msgid "" +"Select a valid choice. That choice is not one of the available choices." +msgstr "Sélectionnez un choix valide. Ce choix ne fait pas partie de ceux disponibles." diff --git a/dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.mo b/dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.mo index 2e8eae72cda924a15ba8abda9f5971e01474dd52..8e47890e72bf91dcd2bfede64baed88b53a577ea 100644 GIT binary patch delta 67 zcmey#`jd47AESVguAzahftiA#sg;3|u7Sy9IYtRyV_ic-T_XbpLjx;Ai^h4FcLWc diff --git a/dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.po b/dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.po index dbe73c9..7ab61a4 100644 --- a/dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.po +++ b/dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.po @@ -1,29 +1,28 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: -# , 2012. +# slafs , 2012 msgid "" msgstr "" "Project-Id-Version: django-selectable\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-02-17 14:18-0500\n" -"PO-Revision-Date: 2012-10-11 12:50+0000\n" +"POT-Creation-Date: 2012-10-06 15:02-0400\n" +"PO-Revision-Date: 2013-11-20 10:18+0000\n" "Last-Translator: slafs \n" -"Language-Team: Polish (http://www.transifex.com/projects/p/django-selectable/" -"language/pl/)\n" -"Language: pl\n" +"Language-Team: Polish (http://www.transifex.com/projects/p/django-selectable/language/pl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " -"|| n%100>=20) ? 1 : 2);\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -#: base.py:117 +#: base.py:115 msgid "Show more results" msgstr "Pokaż więcej wyników" -#: forms/fields.py:22 forms/fields.py:69 -msgid "Select a valid choice. That choice is not one of the available choices." +#: forms/fields.py:19 forms/fields.py:63 +msgid "" +"Select a valid choice. That choice is not one of the available choices." msgstr "Dokonaj poprawnego wyboru. Ten wybór nie jest jednym z dostępnych." diff --git a/dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.mo b/dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.mo index 1d497c097d6d66cee10591dc6fdba84935e1309c..6afef1b6a3c19c040ddce60da88df74b1bb98101 100644 GIT binary patch delta 215 zcmbQldY5&AiRb}F28P`X3=AGXT*Jh`AP%IDPV_t~V5Dnkple{JU}$P(V5DncGFgsM zLd#g!&`{UNK*7+!%FsgF00_8z5{pZ8Ly8jfigOZ6@{6n#d=rbZ6?`VgFlt8!=4dKtWR#Q?Sn2DRmzV36fV5?%rB>)A=jZAd6y;~7CYKcJ7wD&C yCFZ5)>lUZx00k10a#HnkfC8n7>8bh!CGk!{`kIqhGe-0KKn1N7z@n2`nR)@#Cqcsi delta 135 zcmcc1I*E0HiD)S!1H*0x1_loxHe_O85C_s#6FrX#80#7s=^C0V7@AlaTId>>PL^Yo zn9Rq>DQjtEXsK-g1YACe#U;8SMTvREIf*6tMOF&AIg=9@wN-o^{oF$x-CY%Ie0&^o iGWGI{((QnX^U_Nb(^IV!3QFRgf+jy>jGi3G)B^xFog=&e diff --git a/dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.po b/dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.po index 9591094..3999877 100644 --- a/dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.po +++ b/dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.po @@ -1,26 +1,27 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: msgid "" msgstr "" "Project-Id-Version: django-selectable\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-02-17 14:18-0500\n" -"PO-Revision-Date: 2012-10-06 19:19+0000\n" -"Last-Translator: mlavin \n" -"Language-Team: LANGUAGE \n" -"Language: pt_BR\n" +"POT-Creation-Date: 2012-10-06 15:02-0400\n" +"PO-Revision-Date: 2013-11-20 10:18+0000\n" +"Last-Translator: Mark Lavin \n" +"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/django-selectable/language/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Language: pt_BR\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: base.py:117 +#: base.py:115 msgid "Show more results" msgstr "Mostrar mais resultados" -#: forms/fields.py:22 forms/fields.py:69 -msgid "Select a valid choice. That choice is not one of the available choices." +#: forms/fields.py:19 forms/fields.py:63 +msgid "" +"Select a valid choice. That choice is not one of the available choices." msgstr "Selecione uma escolha valida. Esta escolha não é uma das disponíveis." diff --git a/dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.mo b/dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..7bdf49c1a7020714332585708569166726290569 GIT binary patch literal 709 zcmZXQ&1(}u6u`%?%iiqS!#lRfWOrjNVPiy$trQv!ZS<kVWJYvW8^x7`clu z3KrtP=7{^;c*{SW5Ez18$9N93k*6e77eIJO4NA{Y7w(j2ZRBs zEGWrAHNi$4Sci$2ZN_|>Vsd07Qe9{%%|Ub1j;$e8t+uc=U2V{ZTpOWe0F5msLq*Ma zXx6}4=ZVsmR!k@w^maQmw59@Zs`qQOtU0q-M(;4o1IT-Rp86j3mcYLrczNnAdLAay zb>0zK`&r&K>iabBgNJ#As~(E8#*C#k&7_H$RXV`G_Cy@BUI;f-!lhAdjflf)=gq2WD)PqwBfe&pteE_v%GA2 zlWV#x>#8AS7(iiD*s}!!7|(L5aa*&Pt|;9y0mx+Rnv!3HiCOZB4*C1(>uGYp5aO!o)r?u*gha}1t-`1Ys&Z1`hmICwR2)7S6u6@w&w^K*3YJ$?ISc(6A*-bvr> FlYisY?+E|^ literal 0 HcmV?d00001 diff --git a/dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.po b/dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.po new file mode 100644 index 0000000..ae693db --- /dev/null +++ b/dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.po @@ -0,0 +1,28 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# mozillazg , 2013 +msgid "" +msgstr "" +"Project-Id-Version: django-selectable\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-10-06 15:02-0400\n" +"PO-Revision-Date: 2013-11-21 05:08+0000\n" +"Last-Translator: mozillazg \n" +"Language-Team: Chinese (China) (http://www.transifex.com/projects/p/django-selectable/language/zh_CN/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: zh_CN\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: base.py:115 +msgid "Show more results" +msgstr "显示更多结果" + +#: forms/fields.py:19 forms/fields.py:63 +msgid "" +"Select a valid choice. That choice is not one of the available choices." +msgstr "请选择一个有效的选项。当前选项无效。" diff --git a/dep/django-selectable/selectable/registry.py b/dep/django-selectable/selectable/registry.py index 93c77d8..be1af0b 100644 --- a/dep/django-selectable/selectable/registry.py +++ b/dep/django-selectable/selectable/registry.py @@ -40,16 +40,23 @@ def autodiscover(): import copy from django.conf import settings - from django.utils.importlib import import_module - from django.utils.module_loading import module_has_submodule - - for app in settings.INSTALLED_APPS: - mod = import_module(app) - # Attempt to import the app's lookups module. - try: - before_import_registry = copy.copy(registry._registry) - import_module('%s.lookups' % app) - except: - registry._registry = before_import_registry - if module_has_submodule(mod, 'lookups'): - raise + + try: + from django.utils.module_loading import autodiscover_modules + except ImportError: + from django.utils.importlib import import_module + from django.utils.module_loading import module_has_submodule + + def autodiscover_modules(submod, **kwargs): + for app_name in settings.INSTALLED_APPS: + mod = import_module(app_name) + try: + before_import_registry = copy.copy(registry._registry) + import_module('%s.lookups' % app_name) + except: + registry._registry = before_import_registry + if module_has_submodule(mod, 'lookups'): + raise + + # Attempt to import the app's lookups module. + autodiscover_modules('lookups', register_to=registry) diff --git a/dep/django-selectable/selectable/static/selectable/css/dj.selectable.css b/dep/django-selectable/selectable/static/selectable/css/dj.selectable.css index 2f3e473..4501ba6 100644 --- a/dep/django-selectable/selectable/static/selectable/css/dj.selectable.css +++ b/dep/django-selectable/selectable/static/selectable/css/dj.selectable.css @@ -1,9 +1,9 @@ /* * django-selectable UI widget CSS - * Source: https://bitbucket.org/mlavin/django-selectable + * Source: https://github.com/mlavin/django-selectable * Docs: http://django-selectable.readthedocs.org/ * - * Copyright 2010-2013, Mark Lavin + * Copyright 2010-2014, Mark Lavin * BSD License * */ diff --git a/dep/django-selectable/selectable/static/selectable/js/jquery.dj.selectable.js b/dep/django-selectable/selectable/static/selectable/js/jquery.dj.selectable.js index c931cb1..1de6c53 100644 --- a/dep/django-selectable/selectable/static/selectable/js/jquery.dj.selectable.js +++ b/dep/django-selectable/selectable/static/selectable/js/jquery.dj.selectable.js @@ -1,14 +1,14 @@ /*jshint trailing:true, indent:4*/ /* * django-selectable UI widget - * Source: https://bitbucket.org/mlavin/django-selectable + * Source: https://github.com/mlavin/django-selectable * Docs: http://django-selectable.readthedocs.org/ * * Depends: - * - jQuery 1.4.4+ + * - jQuery 1.7+ * - jQuery UI 1.8 widget factory * - * Copyright 2010-2013, Mark Lavin + * Copyright 2010-2014, Mark Lavin * BSD License * */ @@ -313,7 +313,7 @@ }, close: function (event) { var page = $(this.element).data('page'); - if (page != null) { + if (page !== null) { return; } // Call super trigger @@ -332,26 +332,13 @@ }; function djangoAdminPatches() { - /* Monkey-patch Django's dynamic formset, if defined */ - if (typeof(django) !== "undefined" && typeof(django.jQuery) !== "undefined") { - if (django.jQuery.fn.formset) { - var oldformset = django.jQuery.fn.formset; - django.jQuery.fn.formset = function (opts) { - var options = $.extend({}, opts); - var addedevent = function (row) { - window.bindSelectables($(row)); - }; - var added = null; - if (options.added) { - // Wrap previous added function and include call to bindSelectables - var oldadded = options.added; - added = function (row) { oldadded(row); addedevent(row); }; - } - options.added = added || addedevent; - return oldformset.call(this, options); - }; - } - } + /* Listen for new rows being added to the dynamic inlines. + Requires Django 1.5+ */ + $('body').on('click', '.add-row', function (e) { + var wrapper = $(this).parents('.inline-related'), + newRow = $('.form-row:not(.empty-form)', wrapper).last(); + window.bindSelectables(newRow); + }); /* Monkey-patch Django's dismissAddAnotherPopup(), if defined */ if (typeof(dismissAddAnotherPopup) !== "undefined" && diff --git a/dep/django-selectable/selectable/tests/__init__.py b/dep/django-selectable/selectable/tests/__init__.py index 41f4398..afac57e 100644 --- a/dep/django-selectable/selectable/tests/__init__.py +++ b/dep/django-selectable/selectable/tests/__init__.py @@ -1,30 +1,34 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible -from selectable.base import ModelLookup -from selectable.registry import registry +from ..base import ModelLookup +from ..registry import registry +@python_2_unicode_compatible class Thing(models.Model): name = models.CharField(max_length=100) description = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class OtherThing(models.Model): name = models.CharField(max_length=100) thing = models.ForeignKey(Thing) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class ManyThing(models.Model): name = models.CharField(max_length=100) things = models.ManyToManyField(Thing) - def __unicode__(self): + def __str__(self): return self.name @@ -36,11 +40,11 @@ class ThingLookup(ModelLookup): registry.register(ThingLookup) -from selectable.tests.base import * -from selectable.tests.decorators import * -from selectable.tests.fields import * -from selectable.tests.functests import * -from selectable.tests.forms import * -from selectable.tests.templatetags import * -from selectable.tests.views import * -from selectable.tests.widgets import * +from .test_base import * +from .test_decorators import * +from .test_fields import * +from .test_functional import * +from .test_forms import * +from .test_templatetags import * +from .test_views import * +from .test_widgets import * diff --git a/dep/django-selectable/selectable/tests/base.py b/dep/django-selectable/selectable/tests/base.py index dfaba10..fdb4f16 100644 --- a/dep/django-selectable/selectable/tests/base.py +++ b/dep/django-selectable/selectable/tests/base.py @@ -5,19 +5,10 @@ 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', -) +from ..base import ModelLookup +from . import Thing def as_xml(html): @@ -69,120 +60,4 @@ class BaseSelectableTestCase(TestCase): 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 = "" - 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 = "" - 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) + search_fields = ('name__icontains', ) \ No newline at end of file diff --git a/dep/django-selectable/selectable/tests/test_base.py b/dep/django-selectable/selectable/tests/test_base.py new file mode 100644 index 0000000..2b1581f --- /dev/null +++ b/dep/django-selectable/selectable/tests/test_base.py @@ -0,0 +1,131 @@ +from __future__ import unicode_literals + +from django.core.urlresolvers import reverse +from django.utils.html import escape +from django.utils.safestring import SafeData, mark_safe + +from ..base import ModelLookup +from . import Thing +from .base import BaseSelectableTestCase, SimpleModelLookup + +__all__ = ( + 'ModelLookupTestCase', + 'MultiFieldLookupTestCase', + 'LookupEscapingTestCase', +) + + +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 = "" + 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 = "" + 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) diff --git a/dep/django-selectable/selectable/tests/test_decorators.py b/dep/django-selectable/selectable/tests/test_decorators.py new file mode 100644 index 0000000..a9f569f --- /dev/null +++ b/dep/django-selectable/selectable/tests/test_decorators.py @@ -0,0 +1,94 @@ +try: + from unittest.mock import Mock +except ImportError: + from mock import Mock + +from ..decorators import ajax_required, login_required, staff_member_required +from .base import BaseSelectableTestCase, SimpleModelLookup + + +__all__ = ( + 'AjaxRequiredLookupTestCase', + 'LoginRequiredLookupTestCase', + 'StaffRequiredLookupTestCase', +) + + +class AjaxRequiredLookupTestCase(BaseSelectableTestCase): + + def setUp(self): + self.lookup = ajax_required(SimpleModelLookup)() + + def test_ajax_call(self): + "Ajax call should yield a successful response." + request = Mock() + request.is_ajax = lambda: True + response = self.lookup.results(request) + self.assertTrue(response.status_code, 200) + + def test_non_ajax_call(self): + "Non-Ajax call should yield a bad request response." + request = Mock() + request.is_ajax = lambda: False + response = self.lookup.results(request) + self.assertEqual(response.status_code, 400) + + +class LoginRequiredLookupTestCase(BaseSelectableTestCase): + + def setUp(self): + self.lookup = login_required(SimpleModelLookup)() + + def test_authenicated_call(self): + "Authenicated call should yield a successful response." + request = Mock() + user = Mock() + user.is_authenticated = lambda: True + request.user = user + response = self.lookup.results(request) + self.assertTrue(response.status_code, 200) + + def test_non_authenicated_call(self): + "Non-Authenicated call should yield an unauthorized response." + request = Mock() + user = Mock() + user.is_authenticated = lambda: False + request.user = user + response = self.lookup.results(request) + self.assertEqual(response.status_code, 401) + + +class StaffRequiredLookupTestCase(BaseSelectableTestCase): + + def setUp(self): + self.lookup = staff_member_required(SimpleModelLookup)() + + def test_staff_member_call(self): + "Staff member call should yield a successful response." + request = Mock() + user = Mock() + user.is_authenticated = lambda: True + user.is_staff = True + request.user = user + response = self.lookup.results(request) + self.assertTrue(response.status_code, 200) + + def test_authenicated_but_not_staff(self): + "Authenicated but non staff call should yield a forbidden response." + request = Mock() + user = Mock() + user.is_authenticated = lambda: True + user.is_staff = False + request.user = user + response = self.lookup.results(request) + self.assertTrue(response.status_code, 403) + + def test_non_authenicated_call(self): + "Non-Authenicated call should yield an unauthorized response." + request = Mock() + user = Mock() + user.is_authenticated = lambda: False + user.is_staff = False + request.user = user + response = self.lookup.results(request) + self.assertEqual(response.status_code, 401) diff --git a/dep/django-selectable/selectable/tests/test_fields.py b/dep/django-selectable/selectable/tests/test_fields.py new file mode 100644 index 0000000..e962003 --- /dev/null +++ b/dep/django-selectable/selectable/tests/test_fields.py @@ -0,0 +1,134 @@ +from django import forms + +from selectable.forms import fields, widgets +from selectable.tests import ThingLookup +from selectable.tests.base import BaseSelectableTestCase + + +__all__ = ( + 'AutoCompleteSelectFieldTestCase', + 'AutoCompleteSelectMultipleFieldTestCase', +) + +class FieldTestMixin(object): + field_cls = None + lookup_cls = None + + def get_field_instance(self, allow_new=False, limit=None, widget=None): + return self.field_cls(self.lookup_cls, allow_new=allow_new, limit=limit, widget=widget) + + def test_init(self): + field = self.get_field_instance() + self.assertEqual(field.lookup_class, self.lookup_cls) + + def test_init_with_limit(self): + field = self.get_field_instance(limit=10) + self.assertEqual(field.limit, 10) + self.assertEqual(field.widget.limit, 10) + + def test_clean(self): + self.fail('This test has not yet been written') + + def test_dotted_path(self): + """ + Ensure lookup_class can be imported from a dotted path. + """ + dotted_path = '.'.join([self.lookup_cls.__module__, self.lookup_cls.__name__]) + field = self.field_cls(dotted_path) + self.assertEqual(field.lookup_class, self.lookup_cls) + + def test_invalid_dotted_path(self): + """ + An invalid lookup_class dotted path should raise an ImportError. + """ + with self.assertRaises(ImportError): + self.field_cls('this.is.an.invalid.path') + + def test_dotted_path_wrong_type(self): + """ + lookup_class must be a subclass of LookupBase. + """ + dotted_path = 'selectable.forms.fields.AutoCompleteSelectField' + with self.assertRaises(TypeError): + self.field_cls(dotted_path) + +class AutoCompleteSelectFieldTestCase(BaseSelectableTestCase, FieldTestMixin): + field_cls = fields.AutoCompleteSelectField + lookup_cls = ThingLookup + + def test_clean(self): + thing = self.create_thing() + field = self.get_field_instance() + value = field.clean([thing.name, thing.id]) + self.assertEqual(thing, value) + + def test_new_not_allowed(self): + field = self.get_field_instance() + value = self.get_random_string() + self.assertRaises(forms.ValidationError, field.clean, [value, '']) + + def test_new_allowed(self): + field = self.get_field_instance(allow_new=True) + value = self.get_random_string() + value = field.clean([value, '']) + self.assertTrue(isinstance(value, ThingLookup.model)) + + def test_default_widget(self): + field = self.get_field_instance() + self.assertTrue(isinstance(field.widget, widgets.AutoCompleteSelectWidget)) + + def test_alternate_widget(self): + widget_cls = widgets.AutoComboboxWidget + field = self.get_field_instance(widget=widget_cls) + self.assertTrue(isinstance(field.widget, widget_cls)) + + def test_alternate_widget_instance(self): + widget = widgets.AutoComboboxWidget(self.lookup_cls) + field = self.get_field_instance(widget=widget) + self.assertTrue(isinstance(field.widget, widgets.AutoComboboxWidget)) + + def test_invalid_pk(self): + field = self.get_field_instance() + value = self.get_random_string() + self.assertRaises(forms.ValidationError, field.clean, [value, 'XXX']) + + +class AutoCompleteSelectMultipleFieldTestCase(BaseSelectableTestCase, FieldTestMixin): + field_cls = fields.AutoCompleteSelectMultipleField + lookup_cls = ThingLookup + + def get_field_instance(self, limit=None, widget=None): + return self.field_cls(self.lookup_cls, limit=limit, widget=widget) + + def test_clean(self): + thing = self.create_thing() + field = self.get_field_instance() + value = field.clean([thing.id]) + self.assertEqual([thing], value) + + def test_clean_multiple(self): + thing = self.create_thing() + other_thing = self.create_thing() + field = self.get_field_instance() + ids = [thing.id, other_thing.id] + value = field.clean(ids) + self.assertEqual([thing, other_thing], value) + + def test_default_widget(self): + field = self.get_field_instance() + self.assertTrue(isinstance(field.widget, widgets.AutoCompleteSelectMultipleWidget)) + + def test_alternate_widget(self): + widget_cls = widgets.AutoComboboxSelectMultipleWidget + field = self.get_field_instance(widget=widget_cls) + self.assertTrue(isinstance(field.widget, widget_cls)) + + def test_alternate_widget_instance(self): + widget = widgets.AutoComboboxSelectMultipleWidget(self.lookup_cls) + field = self.get_field_instance(widget=widget) + self.assertTrue(isinstance(field.widget, widgets.AutoComboboxSelectMultipleWidget)) + + def test_invalid_pk(self): + field = self.get_field_instance() + value = self.get_random_string() + self.assertRaises(forms.ValidationError, field.clean, ['XXX', ]) diff --git a/dep/django-selectable/selectable/tests/test_forms.py b/dep/django-selectable/selectable/tests/test_forms.py new file mode 100644 index 0000000..7bce96e --- /dev/null +++ b/dep/django-selectable/selectable/tests/test_forms.py @@ -0,0 +1,86 @@ +from django.conf import settings + +from ..forms import BaseLookupForm +from .base import BaseSelectableTestCase, PatchSettingsMixin + + +__all__ = ( + 'BaseLookupFormTestCase', +) + + +class BaseLookupFormTestCase(PatchSettingsMixin, BaseSelectableTestCase): + + def get_valid_data(self): + data = { + 'term': 'foo', + 'limit': 10, + } + return data + + def test_valid_data(self): + data = self.get_valid_data() + form = BaseLookupForm(data) + self.assertTrue(form.is_valid(), "%s" % form.errors) + + def test_invalid_limit(self): + """ + Test giving the form an invalid limit. + """ + + data = self.get_valid_data() + data['limit'] = 'bar' + form = BaseLookupForm(data) + self.assertFalse(form.is_valid()) + + def test_no_limit(self): + """ + If SELECTABLE_MAX_LIMIT is set and limit is not given then + the form will return SELECTABLE_MAX_LIMIT. + """ + + data = self.get_valid_data() + if 'limit' in data: + del data['limit'] + form = BaseLookupForm(data) + self.assertTrue(form.is_valid(), "%s" % form.errors) + self.assertEqual(form.cleaned_data['limit'], settings.SELECTABLE_MAX_LIMIT) + + def test_no_max_set(self): + """ + If SELECTABLE_MAX_LIMIT is not set but given then the form + will return the given limit. + """ + + settings.SELECTABLE_MAX_LIMIT = None + data = self.get_valid_data() + form = BaseLookupForm(data) + self.assertTrue(form.is_valid(), "%s" % form.errors) + if 'limit' in data: + self.assertTrue(form.cleaned_data['limit'], data['limit']) + + def test_no_max_set_not_given(self): + """ + If SELECTABLE_MAX_LIMIT is not set and not given then the form + will return no limit. + """ + + settings.SELECTABLE_MAX_LIMIT = None + data = self.get_valid_data() + if 'limit' in data: + del data['limit'] + form = BaseLookupForm(data) + self.assertTrue(form.is_valid(), "%s" % form.errors) + self.assertFalse(form.cleaned_data.get('limit')) + + def test_over_limit(self): + """ + If SELECTABLE_MAX_LIMIT is set and limit given is greater then + the form will return SELECTABLE_MAX_LIMIT. + """ + + data = self.get_valid_data() + data['limit'] = settings.SELECTABLE_MAX_LIMIT + 100 + form = BaseLookupForm(data) + self.assertTrue(form.is_valid(), "%s" % form.errors) + self.assertEqual(form.cleaned_data['limit'], settings.SELECTABLE_MAX_LIMIT) diff --git a/dep/django-selectable/selectable/tests/test_functional.py b/dep/django-selectable/selectable/tests/test_functional.py new file mode 100644 index 0000000..01bf9bf --- /dev/null +++ b/dep/django-selectable/selectable/tests/test_functional.py @@ -0,0 +1,538 @@ +""" +Larger functional tests for fields and widgets. +""" +from __future__ import unicode_literals + +from django import forms + +from ..forms import AutoCompleteSelectField, AutoCompleteSelectMultipleField +from ..forms import AutoCompleteSelectWidget, AutoComboboxSelectWidget +from . import ManyThing, OtherThing, ThingLookup +from .base import BaseSelectableTestCase, parsed_inputs + + +__all__ = ( + 'FuncAutoCompleteSelectTestCase', + 'FuncSelectModelChoiceTestCase', + 'FuncComboboxModelChoiceTestCase', + 'FuncManytoManyMultipleSelectTestCase', + 'FuncFormTestCase', +) + + +class OtherThingForm(forms.ModelForm): + + thing = AutoCompleteSelectField(lookup_class=ThingLookup) + + class Meta(object): + model = OtherThing + + +class FuncAutoCompleteSelectTestCase(BaseSelectableTestCase): + + def setUp(self): + self.test_thing = self.create_thing() + + def test_valid_form(self): + "Valid form using an AutoCompleteSelectField." + data = { + 'name': self.get_random_string(), + 'thing_0': self.test_thing.name, # Text input + 'thing_1': self.test_thing.pk, # Hidden input + } + form = OtherThingForm(data=data) + self.assertTrue(form.is_valid(), str(form.errors)) + + def test_invalid_form_missing_selected_pk(self): + "Invalid form using an AutoCompleteSelectField." + data = { + 'name': self.get_random_string(), + 'thing_0': self.test_thing.name, # Text input + 'thing_1': '', # Hidden input + } + form = OtherThingForm(data=data) + self.assertFalse(form.is_valid(), 'Form should not be valid') + self.assertFalse('name' in form.errors) + self.assertTrue('thing' in form.errors) + + def test_invalid_form_missing_name(self): + "Invalid form using an AutoCompleteSelectField." + data = { + 'name': '', + 'thing_0': self.test_thing.name, # Text input + 'thing_1': self.test_thing.pk, # Hidden input + } + form = OtherThingForm(data=data) + self.assertFalse(form.is_valid(), 'Form should not be valid') + self.assertTrue('name' in form.errors) + self.assertFalse('thing' in form.errors) + + def test_invalid_but_still_selected(self): + "Invalid form should keep selected item." + data = { + 'name': '', + 'thing_0': self.test_thing.name, # Text input + 'thing_1': self.test_thing.pk, # Hidden input + } + form = OtherThingForm(data=data) + self.assertFalse(form.is_valid(), 'Form should not be valid') + rendered_form = form.as_p() + inputs = parsed_inputs(rendered_form) + # Selected text should be populated + thing_0 = inputs['thing_0'][0] + self.assertEqual(thing_0.attributes['value'].value, self.test_thing.name) + # Selected pk should be populated + thing_1 = inputs['thing_1'][0] + self.assertEqual(int(thing_1.attributes['value'].value), self.test_thing.pk) + + def test_populate_from_model(self): + "Populate from existing model." + other_thing = OtherThing.objects.create(thing=self.test_thing, name='a') + form = OtherThingForm(instance=other_thing) + rendered_form = form.as_p() + inputs = parsed_inputs(rendered_form) + # Selected text should be populated + thing_0 = inputs['thing_0'][0] + self.assertEqual(thing_0.attributes['value'].value, self.test_thing.name) + # Selected pk should be populated + thing_1 = inputs['thing_1'][0] + self.assertEqual(int(thing_1.attributes['value'].value), self.test_thing.pk) + + +class SelectWidgetForm(forms.ModelForm): + + class Meta(object): + model = OtherThing + widgets = { + 'thing': AutoCompleteSelectWidget(lookup_class=ThingLookup) + } + + +class FuncSelectModelChoiceTestCase(BaseSelectableTestCase): + """ + Functional tests for AutoCompleteSelectWidget compatibility + with a ModelChoiceField. + """ + + def setUp(self): + self.test_thing = self.create_thing() + + def test_valid_form(self): + "Valid form using an AutoCompleteSelectWidget." + data = { + 'name': self.get_random_string(), + 'thing_0': self.test_thing.name, # Text input + 'thing_1': self.test_thing.pk, # Hidden input + } + form = SelectWidgetForm(data=data) + self.assertTrue(form.is_valid(), str(form.errors)) + + def test_missing_pk(self): + "Invalid form (missing required pk) using an AutoCompleteSelectWidget." + data = { + 'name': self.get_random_string(), + 'thing_0': self.test_thing.name, # Text input + 'thing_1': '', # Hidden input missing + } + form = SelectWidgetForm(data=data) + self.assertFalse(form.is_valid()) + self.assertTrue('thing' in form.errors) + + def test_invalid_pk(self): + "Invalid form (invalid pk value) using an AutoCompleteSelectWidget." + data = { + 'name': self.get_random_string(), + 'thing_0': self.test_thing.name, # Text input + 'thing_1': 'XXX', # Hidden input doesn't match a PK + } + form = SelectWidgetForm(data=data) + self.assertFalse(form.is_valid()) + self.assertTrue('thing' in form.errors) + + def test_post_compatibility(self): + """ + If new items are not allowed then the original field + name can be included in the POST with the selected id. + """ + data = { + 'name': self.get_random_string(), + 'thing': self.test_thing.pk, + } + form = SelectWidgetForm(data=data) + self.assertTrue(form.is_valid(), str(form.errors)) + + +class ComboboxSelectWidgetForm(forms.ModelForm): + + class Meta(object): + model = OtherThing + widgets = { + 'thing': AutoComboboxSelectWidget(lookup_class=ThingLookup) + } + + +class FuncComboboxModelChoiceTestCase(BaseSelectableTestCase): + """ + Functional tests for AutoComboboxSelectWidget compatibility + with a ModelChoiceField. + """ + + def setUp(self): + self.test_thing = self.create_thing() + + def test_valid_form(self): + "Valid form using an AutoComboboxSelectWidget." + data = { + 'name': self.get_random_string(), + 'thing_0': self.test_thing.name, # Text input + 'thing_1': self.test_thing.pk, # Hidden input + } + form = ComboboxSelectWidgetForm(data=data) + self.assertTrue(form.is_valid(), str(form.errors)) + + def test_missing_pk(self): + "Invalid form (missing required pk) using an AutoComboboxSelectWidget." + data = { + 'name': self.get_random_string(), + 'thing_0': self.test_thing.name, # Text input + 'thing_1': '', # Hidden input missing + } + form = ComboboxSelectWidgetForm(data=data) + self.assertFalse(form.is_valid()) + self.assertTrue('thing' in form.errors) + + def test_invalid_pk(self): + "Invalid form (invalid pk value) using an AutoComboboxSelectWidget." + data = { + 'name': self.get_random_string(), + 'thing_0': self.test_thing.name, # Text input + 'thing_1': 'XXX', # Hidden input doesn't match a PK + } + form = ComboboxSelectWidgetForm(data=data) + self.assertFalse(form.is_valid()) + self.assertTrue('thing' in form.errors) + + def test_post_compatibility(self): + """ + If new items are not allowed then the original field + name can be included in the POST with the selected id. + """ + data = { + 'name': self.get_random_string(), + 'thing': self.test_thing.pk, + } + form = ComboboxSelectWidgetForm(data=data) + self.assertTrue(form.is_valid(), str(form.errors)) + + +class ManyThingForm(forms.ModelForm): + + things = AutoCompleteSelectMultipleField(lookup_class=ThingLookup) + + class Meta(object): + model = ManyThing + + +class FuncManytoManyMultipleSelectTestCase(BaseSelectableTestCase): + """ + Functional tests for AutoCompleteSelectMultipleField compatibility + with a ManyToManyField. + """ + + def setUp(self): + self.test_thing = self.create_thing() + + def test_valid_form(self): + "Valid form using an AutoCompleteSelectMultipleField." + data = { + 'name': self.get_random_string(), + 'things_0': '', # Text input + 'things_1': [self.test_thing.pk, ], # Hidden inputs + } + form = ManyThingForm(data=data) + self.assertTrue(form.is_valid(), str(form.errors)) + + def test_valid_save(self): + "Saving data from a valid form." + data = { + 'name': self.get_random_string(), + 'things_0': '', # Text input + 'things_1': [self.test_thing.pk, ], # Hidden inputs + } + form = ManyThingForm(data=data) + manything = form.save() + self.assertEqual(manything.name, data['name']) + things = manything.things.all() + self.assertEqual(things.count(), 1) + self.assertTrue(self.test_thing in things) + + def test_not_required(self): + "Valid form where many to many is not required." + data = { + 'name': self.get_random_string(), + 'things_0': '', # Text input + 'things_1': [], # Hidden inputs + } + form = ManyThingForm(data=data) + form.fields['things'].required = False + self.assertTrue(form.is_valid(), str(form.errors)) + + def test_not_required_save(self): + "Saving data when many to many is not required." + data = { + 'name': self.get_random_string(), + 'things_0': '', # Text input + 'things_1': [], # Hidden inputs + } + form = ManyThingForm(data=data) + form.fields['things'].required = False + manything = form.save() + self.assertEqual(manything.name, data['name']) + things = manything.things.all() + self.assertEqual(things.count(), 0) + + def test_has_changed(self): + "Populate intial data from a model." + manything = ManyThing.objects.create(name='Foo') + thing_1 = self.create_thing() + manything.things.add(thing_1) + data = { + 'name': manything.name, + 'things_0': '', # Text input + 'things_1': [thing_1.pk], # Hidden inputs + } + form = ManyThingForm(data=data, instance=manything) + self.assertFalse(form.has_changed(), str(form.changed_data)) + + def test_post_compatibility(self): + """ + If new items are not allowed then the original field + name can be included in the POST with the selected ids. + """ + data = { + 'name': self.get_random_string(), + 'things': [self.test_thing.pk, ], + } + form = ManyThingForm(data=data) + self.assertTrue(form.is_valid(), str(form.errors)) + + +class SimpleForm(forms.Form): + "Non-model form usage." + thing = AutoCompleteSelectField(lookup_class=ThingLookup) + new_thing = AutoCompleteSelectField(lookup_class=ThingLookup, allow_new=True) + things = AutoCompleteSelectMultipleField(lookup_class=ThingLookup) + + +class FuncFormTestCase(BaseSelectableTestCase): + """ + Functional tests for using AutoCompleteSelectField + and AutoCompleteSelectMultipleField outside the context + of a ModelForm. + """ + + def setUp(self): + self.test_thing = self.create_thing() + + def test_blank_new_item(self): + "Regression test for #91. new_thing is required but both are blank." + data = { + 'thing_0': self.test_thing.name, + 'thing_1': self.test_thing.pk, + 'new_thing_0': '', + 'new_thing_1': '', + 'things_0': '', + 'things_1': [self.test_thing.pk, ] + } + form = SimpleForm(data=data) + self.assertFalse(form.is_valid()) + self.assertTrue('new_thing' in form.errors) + + def test_has_changed_with_empty_permitted(self): + """ + Regression test for #92. has_changed fails when there is no initial and + allow_new=False. + """ + data = { + 'thing_0': '', + 'thing_1': self.test_thing.pk, + 'new_thing_0': self.test_thing.name, + 'new_thing_1': self.test_thing.pk, + 'things_0': '', + 'things_1': [self.test_thing.pk, ] + } + form = SimpleForm(data=data, empty_permitted=True) + self.assertTrue(form.has_changed()) + self.assertTrue(form.is_valid(), str(form.errors)) + + def test_not_changed(self): + """ + Regression test for #92. has_changed fails when there is no initial and + allow_new=False. + """ + data = { + 'thing_0': self.test_thing.name, + 'thing_1': self.test_thing.pk, + 'new_thing_0': self.test_thing.name, + 'new_thing_1': self.test_thing.pk, + 'things_0': '', + 'things_1': [self.test_thing.pk, ] + } + initial = { + 'thing': self.test_thing.pk, + 'new_thing': self.test_thing.pk, + 'things': [self.test_thing.pk, ] + } + form = SimpleForm(data=data, initial=initial) + self.assertFalse(form.has_changed()) + self.assertTrue(form.is_valid(), str(form.errors)) + + def test_not_changed_with_empty_permitted(self): + """ + Regression test for #92. has_changed fails when there is no initial and + allow_new=False. + """ + data = { + 'thing_0': '', + 'thing_1': '', + 'new_thing_0': '', + 'new_thing_1': '', + 'things_0': '', + 'things_1': '', + } + initial = { + 'thing': '', + 'new_thing': '', + 'things': '', + } + form = SimpleForm(data=data, initial=initial, empty_permitted=True) + self.assertFalse(form.has_changed(), str(form.changed_data)) + self.assertTrue(form.is_valid(), str(form.errors)) + + def test_no_initial_with_empty_permitted(self): + """ + If empty data is submitted and allowed with no initial then + the form should not be seen as changed. + """ + data = { + 'thing_0': '', + 'thing_1': '', + 'new_thing_0': '', + 'new_thing_1': '', + 'things_0': '', + 'things_1': '', + } + form = SimpleForm(data=data, empty_permitted=True) + self.assertFalse(form.has_changed(), str(form.changed_data)) + self.assertTrue(form.is_valid(), str(form.errors)) + + def test_no_data_with_empty_permitted(self): + """ + If no data is submitted and allowed with no initial then + the form should not be seen as changed. + """ + form = SimpleForm(data={}, empty_permitted=True) + self.assertFalse(form.has_changed(), str(form.changed_data)) + self.assertTrue(form.is_valid(), str(form.errors)) + + def test_select_multiple_changed(self): + """ + Detect changes for a multiple select input with and without + initial data. + """ + data = { + 'thing_0': '', + 'thing_1': '', + 'new_thing_0': '', + 'new_thing_1': '', + 'things_0': '', + 'things_1': [self.test_thing.pk, ] + } + form = SimpleForm(data=data) + self.assertTrue(form.has_changed()) + self.assertTrue('things' in form.changed_data) + + initial = { + 'thing': '', + 'new_thing': '', + 'things': [self.test_thing.pk, ], + } + form = SimpleForm(data=data, initial=initial) + self.assertFalse(form.has_changed(), str(form.changed_data)) + + initial = { + 'thing': '', + 'new_thing': '', + 'things': [], + } + form = SimpleForm(data=data, initial=initial) + self.assertTrue(form.has_changed()) + self.assertTrue('things' in form.changed_data) + + def test_single_select_changed(self): + """ + Detect changes for a single select input with and without + initial data. + """ + data = { + 'thing_0': '', + 'thing_1': self.test_thing.pk, + 'new_thing_0': '', + 'new_thing_1': '', + 'things_0': '', + 'things_1': '' + } + form = SimpleForm(data=data) + self.assertTrue(form.has_changed()) + self.assertTrue('thing' in form.changed_data) + + initial = { + 'thing': self.test_thing.pk, + 'new_thing': '', + 'things': '', + } + form = SimpleForm(data=data, initial=initial) + self.assertFalse(form.has_changed(), str(form.changed_data)) + + initial = { + 'thing': '', + 'new_thing': '', + 'things': '', + } + form = SimpleForm(data=data, initial=initial) + self.assertTrue(form.has_changed()) + self.assertTrue('thing' in form.changed_data) + + def test_new_select_changed(self): + """ + Detect changes for a single select input which allows new items + with and without initial data. + """ + data = { + 'thing_0': '', + 'thing_1': '', + 'new_thing_0': 'Foo', + 'new_thing_1': '', + 'things_0': '', + 'things_1': '' + } + form = SimpleForm(data=data) + self.assertTrue(form.has_changed()) + self.assertTrue('new_thing' in form.changed_data) + + initial = { + 'thing': '', + 'new_thing': ['Foo', None], + 'things': '', + } + form = SimpleForm(data=data, initial=initial) + self.assertFalse(form.has_changed(), str(form.changed_data)) + + initial = { + 'thing': '', + 'new_thing': '', + 'things': '', + } + form = SimpleForm(data=data, initial=initial) + self.assertTrue(form.has_changed()) + self.assertTrue('new_thing' in form.changed_data) diff --git a/dep/django-selectable/selectable/tests/test_templatetags.py b/dep/django-selectable/selectable/tests/test_templatetags.py new file mode 100644 index 0000000..c0042fb --- /dev/null +++ b/dep/django-selectable/selectable/tests/test_templatetags.py @@ -0,0 +1,115 @@ +from django.template import Template, Context + +from .base import BaseSelectableTestCase + +__all__ = ( + 'JqueryTagTestCase', + 'ThemeTagTestCase', +) + + +class JqueryTagTestCase(BaseSelectableTestCase): + + def assertJQueryVersion(self, result, version): + expected = "//ajax.googleapis.com/ajax/libs/jquery/%s/jquery.min.js" % version + self.assertTrue(expected in result) + + def assertUIVersion(self, result, version): + expected = "//ajax.googleapis.com/ajax/libs/jqueryui/%s/jquery-ui.js" % version + self.assertTrue(expected in result) + + def test_render(self): + "Render template tag with default versions." + template = Template("{% load selectable_tags %}{% include_jquery_libs %}") + context = Context({}) + result = template.render(context) + self.assertJQueryVersion(result, '1.7.2') + self.assertUIVersion(result, '1.8.23') + + def test_render_jquery_version(self): + "Render template tag with specified jQuery version." + template = Template("{% load selectable_tags %}{% include_jquery_libs '1.4.3' %}") + context = Context({}) + result = template.render(context) + self.assertJQueryVersion(result, '1.4.3') + + def test_render_variable_jquery_version(self): + "Render using jQuery version from the template context." + version = '1.4.3' + template = Template("{% load selectable_tags %}{% include_jquery_libs version %}") + context = Context({'version': version}) + result = template.render(context) + self.assertJQueryVersion(result, '1.4.3') + + def test_render_jquery_ui_version(self): + "Render template tag with specified jQuery UI version." + template = Template("{% load selectable_tags %}{% include_jquery_libs '1.4.3' '1.8.13' %}") + context = Context({}) + result = template.render(context) + self.assertUIVersion(result, '1.8.13') + + def test_render_variable_jquery_ui_version(self): + "Render using jQuery UI version from the template context." + version = '1.8.13' + template = Template("{% load selectable_tags %}{% include_jquery_libs '1.4.3' version %}") + context = Context({'version': version}) + result = template.render(context) + self.assertUIVersion(result, '1.8.13') + + def test_render_no_jquery(self): + "Render template tag without jQuery." + template = Template("{% load selectable_tags %}{% include_jquery_libs '' %}") + context = Context({}) + result = template.render(context) + self.assertTrue('jquery.min.js' not in result) + + def test_render_no_jquery_ui(self): + "Render template tag without jQuery UI." + template = Template("{% load selectable_tags %}{% include_jquery_libs '1.7.2' '' %}") + context = Context({}) + result = template.render(context) + self.assertTrue('jquery-ui.js' not in result) + + +class ThemeTagTestCase(BaseSelectableTestCase): + + def assertUICSS(self, result, theme, version): + expected = "//ajax.googleapis.com/ajax/libs/jqueryui/%s/themes/%s/jquery-ui.css" % (version, theme) + self.assertTrue(expected in result) + + def test_render(self): + "Render template tag with default settings." + template = Template("{% load selectable_tags %}{% include_ui_theme %}") + context = Context({}) + result = template.render(context) + self.assertUICSS(result, 'base', '1.8.23') + + def test_render_version(self): + "Render template tag with alternate version." + template = Template("{% load selectable_tags %}{% include_ui_theme 'base' '1.8.13' %}") + context = Context({}) + result = template.render(context) + self.assertUICSS(result, 'base', '1.8.13') + + def test_variable_version(self): + "Render using version from content variable." + version = '1.8.13' + template = Template("{% load selectable_tags %}{% include_ui_theme 'base' version %}") + context = Context({'version': version}) + result = template.render(context) + self.assertUICSS(result, 'base', version) + + def test_render_theme(self): + "Render template tag with alternate theme." + template = Template("{% load selectable_tags %}{% include_ui_theme 'ui-lightness' %}") + context = Context({}) + result = template.render(context) + self.assertUICSS(result, 'ui-lightness', '1.8.23') + + def test_variable_theme(self): + "Render using theme from content variable." + theme = 'ui-lightness' + template = Template("{% load selectable_tags %}{% include_ui_theme theme %}") + context = Context({'theme': theme}) + result = template.render(context) + self.assertUICSS(result, theme, '1.8.23') diff --git a/dep/django-selectable/selectable/tests/test_views.py b/dep/django-selectable/selectable/tests/test_views.py new file mode 100644 index 0000000..4dc20c4 --- /dev/null +++ b/dep/django-selectable/selectable/tests/test_views.py @@ -0,0 +1,91 @@ +from __future__ import division + +import json + +from django.conf import settings +from django.core.urlresolvers import reverse + +from . import ThingLookup +from .base import BaseSelectableTestCase, PatchSettingsMixin + + +__all__ = ( + 'SelectableViewTest', +) + + +class SelectableViewTest(PatchSettingsMixin, BaseSelectableTestCase): + + def setUp(self): + super(SelectableViewTest, self).setUp() + self.url = ThingLookup.url() + self.lookup = ThingLookup() + self.thing = self.create_thing() + self.other_thing = self.create_thing() + + def test_response_type(self): + response = self.client.get(self.url) + self.assertEqual(response['Content-Type'], 'application/json') + + def test_response_keys(self): + response = self.client.get(self.url) + data = json.loads(response.content.decode('utf-8')) + for result in data.get('data'): + self.assertTrue('id' in result) + self.assertTrue('value' in result) + self.assertTrue('label' in result) + + def test_no_term_lookup(self): + data = {} + response = self.client.get(self.url, data) + data = json.loads(response.content.decode('utf-8')) + self.assertEqual(len(data), 2) + + def test_simple_term_lookup(self): + data = {'term': self.thing.name} + response = self.client.get(self.url, data) + data = json.loads(response.content.decode('utf-8')) + self.assertEqual(len(data), 2) + self.assertEqual(len(data.get('data')), 1) + + def test_unknown_lookup(self): + unknown_url = reverse('selectable-lookup', args=["XXXXXXX"]) + response = self.client.get(unknown_url) + self.assertEqual(response.status_code, 404) + + def test_basic_limit(self): + for i in range(settings.SELECTABLE_MAX_LIMIT): + self.create_thing(data={'name': 'Thing%s' % i}) + response = self.client.get(self.url) + data = json.loads(response.content.decode('utf-8')) + self.assertEqual(len(data.get('data')), settings.SELECTABLE_MAX_LIMIT) + meta = data.get('meta') + self.assertTrue('next_page' in meta) + + def test_get_next_page(self): + for i in range(settings.SELECTABLE_MAX_LIMIT * 2): + self.create_thing(data={'name': 'Thing%s' % i}) + data = {'term': 'Thing', 'page': 2} + response = self.client.get(self.url, data) + data = json.loads(response.content.decode('utf-8')) + self.assertEqual(len(data.get('data')), settings.SELECTABLE_MAX_LIMIT) + # No next page + meta = data.get('meta') + self.assertFalse('next_page' in meta) + + def test_request_more_than_max(self): + for i in range(settings.SELECTABLE_MAX_LIMIT): + self.create_thing(data={'name': 'Thing%s' % i}) + data = {'term': '', 'limit': settings.SELECTABLE_MAX_LIMIT * 2} + response = self.client.get(self.url) + data = json.loads(response.content.decode('utf-8')) + self.assertEqual(len(data.get('data')), settings.SELECTABLE_MAX_LIMIT) + + def test_request_less_than_max(self): + for i in range(settings.SELECTABLE_MAX_LIMIT): + self.create_thing(data={'name': 'Thing%s' % i}) + new_limit = settings.SELECTABLE_MAX_LIMIT // 2 + data = {'term': '', 'limit': new_limit} + response = self.client.get(self.url, data) + data = json.loads(response.content.decode('utf-8')) + self.assertEqual(len(data.get('data')), new_limit) diff --git a/dep/django-selectable/selectable/tests/test_widgets.py b/dep/django-selectable/selectable/tests/test_widgets.py new file mode 100644 index 0000000..58a7d3a --- /dev/null +++ b/dep/django-selectable/selectable/tests/test_widgets.py @@ -0,0 +1,477 @@ +import json + +from django import forms +from django.utils.http import urlencode + +from ..compat import urlparse +from ..forms import widgets +from . import Thing, ThingLookup +from .base import BaseSelectableTestCase, parsed_inputs + + +__all__ = ( + 'AutoCompleteWidgetTestCase', + 'AutoCompleteSelectWidgetTestCase', + 'AutoComboboxWidgetTestCase', + 'AutoComboboxSelectWidgetTestCase', + 'AutoCompleteSelectMultipleWidgetTestCase', + 'AutoComboboxSelectMultipleWidgetTestCase', +) + + +class WidgetTestMixin(object): + widget_cls = None + lookup_cls = None + + def get_widget_instance(self, **kwargs): + return self.__class__.widget_cls(self.__class__.lookup_cls, **kwargs) + + def test_init(self): + widget = self.get_widget_instance() + self.assertEqual(widget.lookup_class, self.__class__.lookup_cls) + + def test_dotted_path(self): + """ + Ensure lookup_class can be imported from a dotted path. + """ + dotted_path = '.'.join([self.__class__.lookup_cls.__module__, self.__class__.lookup_cls.__name__]) + widget = self.__class__.widget_cls(dotted_path) + self.assertEqual(widget.lookup_class, self.__class__.lookup_cls) + + def test_invalid_dotted_path(self): + """ + An invalid lookup_class dotted path should raise an ImportError. + """ + with self.assertRaises(ImportError): + self.__class__.widget_cls('this.is.an.invalid.path') + + def test_dotted_path_wrong_type(self): + """ + lookup_class must be a subclass of LookupBase. + """ + dotted_path = 'selectable.forms.widgets.AutoCompleteWidget' + with self.assertRaises(TypeError): + self.__class__.widget_cls(dotted_path) + + +class AutoCompleteWidgetTestCase(BaseSelectableTestCase, WidgetTestMixin): + widget_cls = widgets.AutoCompleteWidget + lookup_cls = ThingLookup + + def test_build_attrs(self): + widget = self.get_widget_instance() + attrs = widget.build_attrs() + self.assertTrue('data-selectable-url' in attrs) + self.assertTrue('data-selectable-type' in attrs) + self.assertTrue('data-selectable-allow-new' in attrs) + + def test_update_query_parameters(self): + params = {'active': 1} + widget = self.get_widget_instance() + widget.update_query_parameters(params) + attrs = widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertEqual(query, urlencode(params)) + + def test_limit_paramter(self): + widget = self.get_widget_instance(limit=10) + attrs = widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertTrue('limit=10' in query) + + def test_initial_query_parameters(self): + params = {'active': 1} + widget = self.get_widget_instance(query_params=params) + attrs = widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertEqual(query, urlencode(params)) + + def test_build_selectable_options(self): + "Serialize selectable options as json in data attribute." + options = {'autoFocus': True} + widget = self.get_widget_instance(attrs={'data-selectable-options': options}) + attrs = widget.build_attrs() + self.assertTrue('data-selectable-options' in attrs) + self.assertEqual(attrs['data-selectable-options'], json.dumps(options)) + + +class AutoCompleteSelectWidgetTestCase(BaseSelectableTestCase, WidgetTestMixin): + widget_cls = widgets.AutoCompleteSelectWidget + lookup_cls = ThingLookup + + def test_has_complete_widget(self): + widget = self.get_widget_instance() + self.assertEqual(widget.widgets[0].__class__, widgets.AutoCompleteWidget) + + def test_has_hidden_widget(self): + widget = self.get_widget_instance() + self.assertEqual(widget.widgets[1].__class__, forms.HiddenInput) + + def test_hidden_type(self): + widget = self.get_widget_instance() + sub_widget = widget.widgets[1] + attrs = sub_widget.build_attrs() + self.assertTrue('data-selectable-type' in attrs) + self.assertEqual(attrs['data-selectable-type'], 'hidden') + + def test_update_query_parameters(self): + params = {'active': 1} + widget = self.get_widget_instance() + widget.update_query_parameters(params) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertEqual(query, urlencode(params)) + + def test_limit_paramter(self): + widget = self.get_widget_instance(limit=10) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertTrue('limit=10' in query) + + def test_initial_query_parameters(self): + params = {'active': 1} + widget = self.get_widget_instance(query_params=params) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertEqual(query, urlencode(params)) + + def test_build_selectable_options(self): + "Serialize selectable options as json in data attribute." + options = {'autoFocus': True} + widget = self.get_widget_instance(attrs={'data-selectable-options': options}) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + self.assertTrue('data-selectable-options' in attrs) + self.assertEqual(attrs['data-selectable-options'], json.dumps(options)) + + def test_postdata_compatible_with_select(self): + "Checks postdata for values that a select widget would generate." + postdata = {'fruit': '1'} + widget = self.get_widget_instance() + widget_val = widget.value_from_datadict(postdata, [], 'fruit') + self.assertEquals(widget_val, '1') + + +class AutoComboboxWidgetTestCase(BaseSelectableTestCase, WidgetTestMixin): + widget_cls = widgets.AutoComboboxWidget + lookup_cls = ThingLookup + + def test_build_attrs(self): + widget = self.get_widget_instance() + attrs = widget.build_attrs() + self.assertTrue('data-selectable-url' in attrs) + self.assertTrue('data-selectable-type' in attrs) + self.assertTrue('data-selectable-allow-new' in attrs) + + def test_update_query_parameters(self): + params = {'active': 1} + widget = self.get_widget_instance() + widget.update_query_parameters(params) + attrs = widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertEqual(query, urlencode(params)) + + def test_limit_paramter(self): + widget = self.get_widget_instance(limit=10) + attrs = widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertTrue('limit=10' in query) + + def test_initial_query_parameters(self): + params = {'active': 1} + widget = self.get_widget_instance(query_params=params) + attrs = widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertEqual(query, urlencode(params)) + + def test_build_selectable_options(self): + "Serialize selectable options as json in data attribute." + options = {'autoFocus': True} + widget = self.get_widget_instance(attrs={'data-selectable-options': options}) + attrs = widget.build_attrs() + self.assertTrue('data-selectable-options' in attrs) + self.assertEqual(attrs['data-selectable-options'], json.dumps(options)) + + +class AutoComboboxSelectWidgetTestCase(BaseSelectableTestCase, WidgetTestMixin): + widget_cls = widgets.AutoComboboxSelectWidget + lookup_cls = ThingLookup + + def test_has_complete_widget(self): + widget = self.get_widget_instance() + self.assertEqual(widget.widgets[0].__class__, widgets.AutoComboboxWidget) + + def test_has_hidden_widget(self): + widget = self.get_widget_instance() + self.assertEqual(widget.widgets[1].__class__, forms.HiddenInput) + + def test_hidden_type(self): + widget = self.get_widget_instance() + sub_widget = widget.widgets[1] + attrs = sub_widget.build_attrs() + self.assertTrue('data-selectable-type' in attrs) + self.assertEqual(attrs['data-selectable-type'], 'hidden') + + def test_update_query_parameters(self): + params = {'active': 1} + widget = self.get_widget_instance() + widget.update_query_parameters(params) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertEqual(query, urlencode(params)) + + def test_limit_paramter(self): + widget = self.get_widget_instance(limit=10) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertTrue('limit=10' in query) + + def test_initial_query_parameters(self): + params = {'active': 1} + widget = self.get_widget_instance(query_params=params) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertEqual(query, urlencode(params)) + + def test_build_selectable_options(self): + "Serialize selectable options as json in data attribute." + options = {'autoFocus': True} + widget = self.get_widget_instance(attrs={'data-selectable-options': options}) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + self.assertTrue('data-selectable-options' in attrs) + self.assertEqual(attrs['data-selectable-options'], json.dumps(options)) + + +class AutoCompleteSelectMultipleWidgetTestCase(BaseSelectableTestCase, WidgetTestMixin): + widget_cls = widgets.AutoCompleteSelectMultipleWidget + lookup_cls = ThingLookup + + def test_has_complete_widget(self): + widget = self.get_widget_instance() + self.assertEqual(widget.widgets[0].__class__, widgets.AutoCompleteWidget) + + def test_multiple_attr(self): + widget = self.get_widget_instance() + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + self.assertTrue('data-selectable-multiple' in attrs) + self.assertEqual(attrs['data-selectable-multiple'], 'true') + + def test_has_hidden_widget(self): + widget = self.get_widget_instance() + self.assertEqual(widget.widgets[1].__class__, widgets.LookupMultipleHiddenInput) + + def test_hidden_type(self): + widget = self.get_widget_instance() + sub_widget = widget.widgets[1] + attrs = sub_widget.build_attrs() + self.assertTrue('data-selectable-type' in attrs) + self.assertEqual(attrs['data-selectable-type'], 'hidden-multiple') + + def test_render_single(self): + widget = self.get_widget_instance() + val = 4 + rendered_value = widget.render('field_name', val) + inputs = parsed_inputs(rendered_value) + field = inputs['field_name_1'][0] + self.assertEqual(field.attributes['data-selectable-type'].value, 'hidden-multiple') + self.assertEqual(field.attributes['type'].value, 'hidden') + self.assertEqual(int(field.attributes['value'].value), val) + + def test_render_list(self): + widget = self.get_widget_instance() + list_val = [8, 5] + rendered_value = widget.render('field_name', list_val) + inputs = parsed_inputs(rendered_value) + found_values = [] + for field in inputs['field_name_1']: + self.assertEqual(field.attributes['data-selectable-type'].value, 'hidden-multiple') + self.assertEqual(field.attributes['type'].value, 'hidden') + found_values.append(int(field.attributes['value'].value)) + self.assertListEqual(found_values, list_val) + + def test_render_qs(self): + widget = self.get_widget_instance() + t1 = self.create_thing() + t2 = self.create_thing() + qs_val = Thing.objects.filter(pk__in=[t1.pk, t2.pk]).values_list('pk', flat=True) + rendered_value = widget.render('field_name', qs_val) + inputs = parsed_inputs(rendered_value) + found_values = [] + for field in inputs['field_name_1']: + self.assertEqual(field.attributes['data-selectable-type'].value, 'hidden-multiple') + self.assertEqual(field.attributes['type'].value, 'hidden') + found_values.append(int(field.attributes['value'].value)) + self.assertListEqual(found_values, [t1.pk, t2.pk]) + + def test_update_query_parameters(self): + params = {'active': 1} + widget = self.get_widget_instance() + widget.update_query_parameters(params) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertEqual(query, urlencode(params)) + + def test_limit_paramter(self): + widget = self.get_widget_instance(limit=10) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertTrue('limit=10' in query) + + def test_initial_query_parameters(self): + params = {'active': 1} + widget = self.get_widget_instance(query_params=params) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertEqual(query, urlencode(params)) + + def test_build_selectable_options(self): + "Serialize selectable options as json in data attribute." + options = {'autoFocus': True} + widget = self.get_widget_instance(attrs={'data-selectable-options': options}) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + self.assertTrue('data-selectable-options' in attrs) + self.assertEqual(attrs['data-selectable-options'], json.dumps(options)) + + +class AutoComboboxSelectMultipleWidgetTestCase(BaseSelectableTestCase, WidgetTestMixin): + widget_cls = widgets.AutoComboboxSelectMultipleWidget + lookup_cls = ThingLookup + + def test_has_complete_widget(self): + widget = self.get_widget_instance() + self.assertEqual(widget.widgets[0].__class__, widgets.AutoComboboxWidget) + + def test_multiple_attr(self): + widget = self.get_widget_instance() + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + self.assertTrue('data-selectable-multiple' in attrs) + self.assertEqual(attrs['data-selectable-multiple'], 'true') + + def test_has_hidden_widget(self): + widget = self.get_widget_instance() + self.assertEqual(widget.widgets[1].__class__, widgets.LookupMultipleHiddenInput) + + def test_hidden_type(self): + widget = self.get_widget_instance() + sub_widget = widget.widgets[1] + attrs = sub_widget.build_attrs() + self.assertTrue('data-selectable-type' in attrs) + self.assertEqual(attrs['data-selectable-type'], 'hidden-multiple') + + def test_render_single(self): + widget = self.get_widget_instance() + val = 4 + rendered_value = widget.render('field_name', val) + inputs = parsed_inputs(rendered_value) + field = inputs['field_name_1'][0] + self.assertEqual(field.attributes['data-selectable-type'].value, 'hidden-multiple') + self.assertEqual(field.attributes['type'].value, 'hidden') + self.assertEqual(field.attributes['value'].value, str(val)) + + def test_render_list(self): + widget = self.get_widget_instance() + list_val = [8, 5] + rendered_value = widget.render('field_name', list_val) + inputs = parsed_inputs(rendered_value) + found_values = [] + for field in inputs['field_name_1']: + self.assertEqual(field.attributes['data-selectable-type'].value, 'hidden-multiple') + self.assertEqual(field.attributes['type'].value, 'hidden') + found_values.append(int(field.attributes['value'].value)) + self.assertListEqual(found_values, list_val) + + def test_render_qs(self): + widget = self.get_widget_instance() + t1 = self.create_thing() + t2 = self.create_thing() + qs_val = Thing.objects.filter(pk__in=[t1.pk, t2.pk]).values_list('pk', flat=True) + rendered_value = widget.render('field_name', qs_val) + inputs = parsed_inputs(rendered_value) + found_values = [] + for field in inputs['field_name_1']: + self.assertEqual(field.attributes['data-selectable-type'].value, 'hidden-multiple') + self.assertEqual(field.attributes['type'].value, 'hidden') + found_values.append(int(field.attributes['value'].value)) + self.assertListEqual(found_values, [t1.pk, t2.pk]) + + def test_update_query_parameters(self): + params = {'active': 1} + widget = self.get_widget_instance() + widget.update_query_parameters(params) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertEqual(query, urlencode(params)) + + def test_limit_paramter(self): + widget = self.get_widget_instance(limit=10) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertTrue('limit=10' in query) + + def test_initial_query_parameters(self): + params = {'active': 1} + widget = self.get_widget_instance(query_params=params) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + url = attrs['data-selectable-url'] + parse = urlparse(url) + query = parse.query + self.assertEqual(query, urlencode(params)) + + def test_build_selectable_options(self): + "Serialize selectable options as json in data attribute." + options = {'autoFocus': True} + widget = self.get_widget_instance(attrs={'data-selectable-options': options}) + sub_widget = widget.widgets[0] + attrs = sub_widget.build_attrs() + self.assertTrue('data-selectable-options' in attrs) + self.assertEqual(attrs['data-selectable-options'], json.dumps(options)) diff --git a/dep/django-selectable/selectable/tests/urls.py b/dep/django-selectable/selectable/tests/urls.py index ce1a383..ed7cf46 100644 --- a/dep/django-selectable/selectable/tests/urls.py +++ b/dep/django-selectable/selectable/tests/urls.py @@ -1,12 +1,9 @@ -try: - from django.conf.urls import handler404, handler500, patterns, include -except ImportError: - # Django < 1.4 - from django.conf.urls.defaults import handler404, handler500, patterns, include +from django.conf.urls import handler404, handler500, include, url + handler404 = 'selectable.tests.views.test_404' handler500 = 'selectable.tests.views.test_500' -urlpatterns = patterns('', - (r'^selectable-tests/', include('selectable.urls')), -) +urlpatterns = [ + url(r'^selectable-tests/', include('selectable.urls')), +] diff --git a/dep/django-selectable/selectable/tests/views.py b/dep/django-selectable/selectable/tests/views.py index b83651f..f747967 100644 --- a/dep/django-selectable/selectable/tests/views.py +++ b/dep/django-selectable/selectable/tests/views.py @@ -1,100 +1,8 @@ -from __future__ import division - -import json - -from django.conf import settings -from django.core.urlresolvers import reverse from django.http import HttpResponseNotFound, HttpResponseServerError -from selectable.tests import ThingLookup -from selectable.tests.base import BaseSelectableTestCase, PatchSettingsMixin - - -__all__ = ( - 'SelectableViewTest', -) - - def test_404(request): return HttpResponseNotFound() def test_500(request): return HttpResponseServerError() - - -class SelectableViewTest(PatchSettingsMixin, BaseSelectableTestCase): - - def setUp(self): - super(SelectableViewTest, self).setUp() - self.url = ThingLookup.url() - self.lookup = ThingLookup() - self.thing = self.create_thing() - self.other_thing = self.create_thing() - - def test_response_type(self): - response = self.client.get(self.url) - self.assertEqual(response['Content-Type'], 'application/json') - - def test_response_keys(self): - response = self.client.get(self.url) - data = json.loads(response.content.decode('utf-8')) - for result in data.get('data'): - self.assertTrue('id' in result) - self.assertTrue('value' in result) - self.assertTrue('label' in result) - - def test_no_term_lookup(self): - data = {} - response = self.client.get(self.url, data) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(len(data), 2) - - def test_simple_term_lookup(self): - data = {'term': self.thing.name} - response = self.client.get(self.url, data) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(len(data), 2) - self.assertEqual(len(data.get('data')), 1) - - def test_unknown_lookup(self): - unknown_url = reverse('selectable-lookup', args=["XXXXXXX"]) - response = self.client.get(unknown_url) - self.assertEqual(response.status_code, 404) - - def test_basic_limit(self): - for i in range(settings.SELECTABLE_MAX_LIMIT): - self.create_thing(data={'name': 'Thing%s' % i}) - response = self.client.get(self.url) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(len(data.get('data')), settings.SELECTABLE_MAX_LIMIT) - meta = data.get('meta') - self.assertTrue('next_page' in meta) - - def test_get_next_page(self): - for i in range(settings.SELECTABLE_MAX_LIMIT * 2): - self.create_thing(data={'name': 'Thing%s' % i}) - data = {'term': 'Thing', 'page': 2} - response = self.client.get(self.url, data) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(len(data.get('data')), settings.SELECTABLE_MAX_LIMIT) - # No next page - meta = data.get('meta') - self.assertFalse('next_page' in meta) - - def test_request_more_than_max(self): - for i in range(settings.SELECTABLE_MAX_LIMIT): - self.create_thing(data={'name': 'Thing%s' % i}) - data = {'term': '', 'limit': settings.SELECTABLE_MAX_LIMIT * 2} - response = self.client.get(self.url) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(len(data.get('data')), settings.SELECTABLE_MAX_LIMIT) - - def test_request_less_than_max(self): - for i in range(settings.SELECTABLE_MAX_LIMIT): - self.create_thing(data={'name': 'Thing%s' % i}) - new_limit = settings.SELECTABLE_MAX_LIMIT // 2 - data = {'term': '', 'limit': new_limit} - response = self.client.get(self.url, data) - data = json.loads(response.content.decode('utf-8')) - self.assertEqual(len(data.get('data')), new_limit) diff --git a/dep/django-selectable/selectable/urls.py b/dep/django-selectable/selectable/urls.py index ec6a73d..02444b9 100644 --- a/dep/django-selectable/selectable/urls.py +++ b/dep/django-selectable/selectable/urls.py @@ -1,14 +1,15 @@ -try: - from django.conf.urls import handler404, handler500, patterns, url -except ImportError: - # Django < 1.4 - from django.conf.urls.defaults import handler404, handler500, patterns, url +from django.conf.urls import url -from selectable import registry +from . import views +from .compat import LEGACY_AUTO_DISCOVER +if LEGACY_AUTO_DISCOVER: + # Auto-discovery is now handled by the app configuration + from . import registry -registry.autodiscover() + registry.autodiscover() -urlpatterns = patterns('selectable.views', - url(r'^(?P[-\w]+)/$', 'get_lookup', name="selectable-lookup"), -) + +urlpatterns = [ + url(r'^(?P[-\w]+)/$', views.get_lookup, name="selectable-lookup"), +] diff --git a/dep/django-selectable/setup.cfg b/dep/django-selectable/setup.cfg new file mode 100644 index 0000000..6f08d0e --- /dev/null +++ b/dep/django-selectable/setup.cfg @@ -0,0 +1,8 @@ +[bdist_wheel] +universal = 1 + +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/dep/django-selectable/setup.py b/dep/django-selectable/setup.py index db7a6d5..77ed457 100644 --- a/dep/django-selectable/setup.py +++ b/dep/django-selectable/setup.py @@ -19,7 +19,7 @@ setup( author_email='markdlavin@gmail.com', packages=find_packages(exclude=['example']), include_package_data=True, - url='http://bitbucket.org/mlavin/django-selectable', + url='https://github.com/mlavin/django-selectable', license='BSD', description=' '.join(__import__('selectable').__doc__.splitlines()).strip(), classifiers=[ @@ -32,6 +32,7 @@ setup( 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', 'Framework :: Django', 'Development Status :: 5 - Production/Stable', 'Operating System :: OS Independent', -- 2.39.5