mirror of
https://github.com/Hopiu/django-select2.git
synced 2026-03-17 05:50:23 +00:00
Added tests
This commit is contained in:
parent
f80abb10f2
commit
5dfd8553cc
15 changed files with 413 additions and 343 deletions
|
|
@ -14,23 +14,19 @@ env:
|
|||
global:
|
||||
- DISPLAY=:99.0
|
||||
matrix:
|
||||
- DJANGO="Django<1.7,>=1.6"
|
||||
- DJANGO="Django<1.8,>=1.7"
|
||||
- DJANGO="Django<1.9,>=1.8"
|
||||
- DJANGO="-e git+https://github.com/django/django.git@master#egg=Django"
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- env: DJANGO="Django<1.7,>=1.6"
|
||||
- env: DJANGO="-e git+https://github.com/django/django.git@master#egg=Django"
|
||||
- python: "pypy"
|
||||
- python: "pypy3"
|
||||
install:
|
||||
- pip install --upgrade pip
|
||||
- pip install -e .
|
||||
- pip install -r requirements_dev.txt
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2* ]]; then pip install python-memcached; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 3* ]]; then pip install python3-memcached; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 2* ]] || [[ $TRAVIS_PYTHON_VERSION == pypy ]]; then pip install python-memcached; fi
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == 3* ]] || [[ $TRAVIS_PYTHON_VERSION == pypy3 ]]; then pip install python3-memcached; fi
|
||||
- pip install $DJANGO
|
||||
- pip install coveralls
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ Light widgets are normally named, i.e. there is no
|
|||
:py:class:`.ModelSelect2Widget`,
|
||||
:py:class:`.ModelSelect2MultipleWidget`,
|
||||
:py:class:`.HeavySelect2TagWidget`,
|
||||
:py:class:`.ModelSelect2TagWidget`
|
||||
:py:class:`.GenreSelect2TagWidget`
|
||||
|
||||
`Read more`_
|
||||
|
||||
|
|
|
|||
|
|
@ -32,14 +32,14 @@ class Select2Mixin(object):
|
|||
"""
|
||||
|
||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||
attrs = super(Select2Mixin, self).build_attrs(extra_attrs=None, **kwargs)
|
||||
attrs = super(Select2Mixin, self).build_attrs(extra_attrs=extra_attrs, **kwargs)
|
||||
if self.is_required:
|
||||
attrs.setdefault('data-allow-clear', 'false')
|
||||
else:
|
||||
attrs.setdefault('data-allow-clear', 'true')
|
||||
attrs.setdefault('data-placeholder', '')
|
||||
|
||||
attrs.setdefault('data-minimumInputLength', 0)
|
||||
attrs.setdefault('data-minimum-input-length', 0)
|
||||
if 'class' in attrs:
|
||||
attrs['class'] += ' django-select2'
|
||||
else:
|
||||
|
|
@ -47,7 +47,7 @@ class Select2Mixin(object):
|
|||
return attrs
|
||||
|
||||
def render_options(self, choices, selected_choices):
|
||||
output = '<option></option>\n' if not self.is_required else ''
|
||||
output = '<option></option>' if not self.is_required else ''
|
||||
output += super(Select2Mixin, self).render_options(choices, selected_choices)
|
||||
return output
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ class HeavySelect2Mixin(Select2Mixin):
|
|||
self.data_view = kwargs.pop('data_view', None)
|
||||
self.data_url = kwargs.pop('data_url', None)
|
||||
if not (self.data_view or self.data_url):
|
||||
ValueError('You must ether specify "data_view" or "data_url".')
|
||||
raise ValueError('You must ether specify "data_view" or "data_url".')
|
||||
self.userGetValTextFuncName = kwargs.pop('userGetValTextFuncName', 'null')
|
||||
super(HeavySelect2Mixin, self).__init__(**kwargs)
|
||||
|
||||
|
|
@ -96,39 +96,37 @@ class HeavySelect2Mixin(Select2Mixin):
|
|||
return reverse_lazy(self.data_view)
|
||||
|
||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||
attrs = super(HeavySelect2Mixin, self).build_attrs(extra_attrs, **kwargs)
|
||||
attrs = super(HeavySelect2Mixin, self).build_attrs(extra_attrs=extra_attrs, **kwargs)
|
||||
|
||||
# encrypt instance Id
|
||||
widget_id = signing.dumps(id(self))
|
||||
# add widget object to cache
|
||||
cache.set(self._get_cache_key(), self)
|
||||
self.widget_id = signing.dumps(id(self))
|
||||
|
||||
attrs['data-field_id'] = widget_id
|
||||
attrs['data-field_id'] = self.widget_id
|
||||
attrs.setdefault('data-ajax--url', self.get_url())
|
||||
attrs.setdefault('data-ajax--cache', "true")
|
||||
attrs.setdefault('data-ajax--type', "GET")
|
||||
attrs.setdefault('data-minimumInputLength', 2)
|
||||
attrs.setdefault('data-minimum-input-length', 2)
|
||||
|
||||
if 'class' in attrs:
|
||||
attrs['class'] += ' django-select2-heavy'
|
||||
else:
|
||||
attrs['class'] = 'django-select2-heavy'
|
||||
attrs['class'] += ' django-select2-heavy'
|
||||
return attrs
|
||||
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
output = super(HeavySelect2Mixin, self).render(name, value, attrs=attrs, choices=choices)
|
||||
self.set_to_cache()
|
||||
return output
|
||||
|
||||
def _get_cache_key(self):
|
||||
return "%s%s" % (settings.SELECT2_CACHE_PREFIX, id(self))
|
||||
|
||||
def value_from_datadict(self, *args, **kwargs):
|
||||
return super(HeavySelect2Mixin, self).value_from_datadict(*args, **kwargs)
|
||||
def set_to_cache(self):
|
||||
"""Add widget object to Djnago's cache."""
|
||||
cache.set(self._get_cache_key(), self)
|
||||
|
||||
def render_options(self, choices, selected_choices):
|
||||
output = [super(HeavySelect2Mixin, self).render_options(choices, selected_choices)]
|
||||
if isinstance(choices, ModelChoiceIterator):
|
||||
selected_choices = set(choices.choice(obj)
|
||||
for obj in choices.queryset.filter(pk__in=selected_choices))
|
||||
else:
|
||||
selected_choices = set((force_text(v), v) for v in choices if v in selected_choices)
|
||||
for option_label, option_value in selected_choices:
|
||||
output = ['<option></option>' if not self.is_required else '']
|
||||
choices = {(k, v) for k, v in choices if k in selected_choices}
|
||||
selected_choices = {force_text(v) for v in selected_choices}
|
||||
for option_value, option_label in choices:
|
||||
output.append(self.render_option(selected_choices, option_value, option_label))
|
||||
return '\n'.join(output)
|
||||
|
||||
|
|
@ -143,10 +141,10 @@ class HeavySelect2MultipleWidget(HeavySelect2Mixin, forms.SelectMultiple):
|
|||
|
||||
class HeavySelect2TagWidget(HeavySelect2MultipleWidget):
|
||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||
attrs = super(HeavySelect2TagWidget, self).build_attrs(self, extra_attrs, **kwargs)
|
||||
attrs['data-minimumInputLength'] = 1
|
||||
attrs = super(HeavySelect2TagWidget, self).build_attrs(extra_attrs, **kwargs)
|
||||
attrs['data-minimum-input-length'] = 1
|
||||
attrs['data-tags'] = 'true'
|
||||
attrs['data-tokenSeparators'] = [",", " "]
|
||||
attrs['data-token-separators'] = [",", " "]
|
||||
return attrs
|
||||
|
||||
|
||||
|
|
@ -154,9 +152,6 @@ class HeavySelect2TagWidget(HeavySelect2MultipleWidget):
|
|||
|
||||
|
||||
class ModelSelect2Mixin(object):
|
||||
|
||||
"""Mixin for """
|
||||
|
||||
model = None
|
||||
queryset = None
|
||||
search_fields = []
|
||||
|
|
@ -198,6 +193,22 @@ class ModelSelect2Mixin(object):
|
|||
return self.search_fields
|
||||
raise NotImplementedError('%s, must implement "search_fields".' % self.__class__.__name__)
|
||||
|
||||
def render_options(self, choices, selected_choices):
|
||||
output = ['<option></option>' if not self.is_required else '']
|
||||
if isinstance(self.choices, ModelChoiceIterator):
|
||||
if not self.queryset:
|
||||
self.queryset = self.choices.queryset
|
||||
selected_choices = {c for c in selected_choices
|
||||
if c not in self.choices.field.empty_values}
|
||||
choices = {self.choices.choice(obj)
|
||||
for obj in self.choices.queryset.filter(pk__in=selected_choices)}
|
||||
else:
|
||||
choices = {(k, v) for k, v in choices if k in selected_choices}
|
||||
selected_choices = {force_text(v) for v in selected_choices}
|
||||
for option_value, option_label in choices:
|
||||
output.append(self.render_option(selected_choices, option_value, option_label))
|
||||
return '\n'.join(output)
|
||||
|
||||
|
||||
class ModelSelect2Widget(ModelSelect2Mixin, HeavySelect2Widget):
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
[pytest]
|
||||
norecursedirs = env
|
||||
addopts = --tb=short -rxs
|
||||
DJANGO_SETTINGS_MODULE=tests.testapp.settings
|
||||
|
||||
|
|
|
|||
48
setup.py
48
setup.py
|
|
@ -23,50 +23,6 @@ URL = "https://github.com/applegrew/django-select2"
|
|||
VERSION = __import__(PACKAGE).__version__
|
||||
|
||||
|
||||
def getPkgPath():
|
||||
return __import__(PACKAGE).__path__[0] + '/'
|
||||
|
||||
|
||||
def minify(files, outfile, ftype):
|
||||
import requests
|
||||
import io
|
||||
|
||||
content = ''
|
||||
|
||||
for filename in files:
|
||||
with io.open(getPkgPath() + filename, 'r', encoding='utf8') as f:
|
||||
content = content + '\n' + f.read()
|
||||
|
||||
data = {
|
||||
'code': content,
|
||||
'type': ftype,
|
||||
}
|
||||
response = requests.post('http://api.applegrew.com/minify', data)
|
||||
response.raise_for_status()
|
||||
response = response.json()
|
||||
if response['success']:
|
||||
with io.open(getPkgPath() + outfile, 'w', encoding='utf8') as f:
|
||||
f.write(response['compiled_code'])
|
||||
else:
|
||||
raise Exception('%(error_code)s: "%(error)s"' % response)
|
||||
|
||||
|
||||
if len(sys.argv) > 1 and 'sdist' == sys.argv[1]:
|
||||
minify(['static/django_select2/js/select2.js'], 'static/django_select2/js/select2.min.js', 'js')
|
||||
minify(['static/django_select2/js/heavy_data.js'], 'static/django_select2/js/heavy_data.min.js', 'js')
|
||||
minify(['static/django_select2/css/select2.css'], 'static/django_select2/css/select2.min.css', 'css')
|
||||
minify(['static/django_select2/css/select2.css', 'static/django_select2/css/extra.css'],
|
||||
'static/django_select2/css/all.min.css', 'css')
|
||||
minify(['static/django_select2/css/select2.css', 'static/django_select2/css/select2-bootstrap.css'],
|
||||
'static/django_select2/css/select2-bootstrapped.min.css', 'css')
|
||||
minify(
|
||||
[
|
||||
'static/django_select2/css/select2.css',
|
||||
'static/django_select2/css/extra.css',
|
||||
'static/django_select2/css/select2-bootstrap.css'
|
||||
], 'static/django_select2/css/all-bootstrapped.min.css', 'css')
|
||||
|
||||
|
||||
class PyTest(Command):
|
||||
user_options = []
|
||||
|
||||
|
|
@ -102,6 +58,10 @@ setup(
|
|||
"License :: OSI Approved :: Apache Software License",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Framework :: Django :: 1.7",
|
||||
"Framework :: Django :: 1.8",
|
||||
"Framework :: Django",
|
||||
],
|
||||
install_requires=[
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.encoding import smart_text
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
|
||||
from django_select2.types import NO_ERR_RESP
|
||||
from tests.testapp.forms import AlbumForm, ArtistForm
|
||||
|
||||
|
||||
class ViewTestMixin(object):
|
||||
url = ''
|
||||
|
||||
def test_get(self, client):
|
||||
response = client.get(self.url)
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
class TestAutoModelSelect2TagField(object):
|
||||
url = reverse('single_value_model_field')
|
||||
|
||||
def test_no_js_error(self, db, client, live_server, driver, genres):
|
||||
driver.get(live_server + self.url)
|
||||
with pytest.raises(NoSuchElementException):
|
||||
error = driver.find_element_by_xpath('//body[@JSError]')
|
||||
pytest.fail(error.get_attribute('JSError'))
|
||||
|
||||
def test_form(self):
|
||||
form = ArtistForm()
|
||||
assert form
|
||||
|
||||
|
||||
class TestAutoModelSelect2Field(object):
|
||||
def test_form(self, client, artists):
|
||||
artist = artists[0]
|
||||
form = AlbumForm()
|
||||
assert form.as_p()
|
||||
field_id = form.fields['artist'].widget.widget_id
|
||||
url = reverse('django_select2_central_json')
|
||||
response = client.get(url, {'field_id': field_id, 'term': artist.title})
|
||||
assert response.status_code == 200
|
||||
data = json.loads(response.content.decode('utf-8'))
|
||||
assert data['results']
|
||||
assert {'id': artist.pk, 'text': smart_text(artist)} in data['results']
|
||||
assert data['more'] is False
|
||||
assert data['err'] == NO_ERR_RESP
|
||||
188
tests/test_forms.py
Normal file
188
tests/test_forms.py
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import collections
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from django.core import signing
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db.models import QuerySet
|
||||
from django.utils.encoding import force_text
|
||||
from model_mommy import mommy
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
from six import text_type
|
||||
|
||||
from django_select2.cache import cache
|
||||
from django_select2.forms import (
|
||||
HeavySelect2Widget, ModelSelect2TagWidget, ModelSelect2Widget,
|
||||
Select2Widget
|
||||
)
|
||||
from tests.testapp import forms
|
||||
from tests.testapp.forms import NUMBER_CHOICES, HeavySelect2MultipleWidgetForm
|
||||
from tests.testapp.models import Genre
|
||||
|
||||
|
||||
class TestSelect2Mixin(object):
|
||||
url = reverse('select2_widget')
|
||||
form = forms.AlbumSelect2WidgetForm()
|
||||
widget_cls = Select2Widget
|
||||
|
||||
def test_initial_form_class(self):
|
||||
widget = self.widget_cls(attrs={'class': 'my-class'})
|
||||
assert 'my-class' in widget.render('name', None)
|
||||
assert 'django-select2' in widget.render('name', None)
|
||||
|
||||
def test_allow_clear(self, db):
|
||||
required_field = self.form.fields['artist']
|
||||
assert required_field.required is True
|
||||
assert 'data-allow-clear="true"' not in required_field.widget.render('artist', None)
|
||||
assert 'data-allow-clear="false"' in required_field.widget.render('artist', None)
|
||||
assert '<option></option>' not in required_field.widget.render('artist', None)
|
||||
|
||||
not_required_field = self.form.fields['primary_genre']
|
||||
assert not_required_field.required is False
|
||||
assert 'data-allow-clear="true"' in not_required_field.widget.render('primary_genre', None)
|
||||
assert 'data-allow-clear="false"' not in not_required_field.widget.render('primary_genre',
|
||||
None)
|
||||
assert 'data-placeholder' in not_required_field.widget.render('primary_genre', None)
|
||||
assert '<option></option>' in not_required_field.widget.render('primary_genre', None)
|
||||
|
||||
def test_no_js_error(self, db, live_server, driver):
|
||||
driver.get(live_server + self.url)
|
||||
with pytest.raises(NoSuchElementException):
|
||||
error = driver.find_element_by_xpath('//body[@JSError]')
|
||||
pytest.fail(error.get_attribute('JSError'))
|
||||
|
||||
def test_selecting(self, db, live_server, driver):
|
||||
driver.get(live_server + self.url)
|
||||
with pytest.raises(NoSuchElementException):
|
||||
driver.find_element_by_css_selector('.select2-results')
|
||||
elem = driver.find_element_by_css_selector('.select2-selection')
|
||||
elem.click()
|
||||
results = driver.find_element_by_css_selector('.select2-results')
|
||||
assert results.is_displayed() is True
|
||||
elem = results.find_element_by_css_selector('.select2-results__option')
|
||||
elem.click()
|
||||
|
||||
with pytest.raises(NoSuchElementException):
|
||||
error = driver.find_element_by_xpath('//body[@JSError]')
|
||||
pytest.fail(error.get_attribute('JSError'))
|
||||
|
||||
def test_data_url(self):
|
||||
with pytest.raises(ValueError):
|
||||
HeavySelect2Widget()
|
||||
|
||||
widget = HeavySelect2Widget(data_url='/foo/bar')
|
||||
assert widget.get_url() == '/foo/bar'
|
||||
|
||||
|
||||
class TestHeavySelect2Mixin(TestSelect2Mixin):
|
||||
url = reverse('heavy_select2_widget')
|
||||
form = forms.HeavySelect2WidgetForm(initial={'primary_genre': [1]})
|
||||
widget_cls = HeavySelect2Widget
|
||||
|
||||
def test_initial_form_class(self):
|
||||
widget = self.widget_cls(data_view='heavy_data', attrs={'class': 'my-class'})
|
||||
assert 'my-class' in widget.render('name', None)
|
||||
assert 'django-select2' in widget.render('name', None)
|
||||
assert 'django-select2-heavy' in widget.render('name', None), widget.render('name', None)
|
||||
|
||||
def test_selected_option(self, db):
|
||||
not_required_field = self.form.fields['primary_genre']
|
||||
assert not_required_field.required is False
|
||||
assert '<option value="1" selected="selected">One</option>' in \
|
||||
not_required_field.widget.render('primary_genre', 1, choices=NUMBER_CHOICES), \
|
||||
not_required_field.widget.render('primary_genre', 1, choices=NUMBER_CHOICES)
|
||||
|
||||
def test_many_selected_option(self, db, genres):
|
||||
field = HeavySelect2MultipleWidgetForm().fields['genres']
|
||||
widget_output = field.widget.render(
|
||||
'genres', [1, 2],
|
||||
choices=NUMBER_CHOICES)
|
||||
selected_option = '<option value="{pk}" selected="selected">{value}</option>'.format(pk=1, value='One')
|
||||
selected_option2 = '<option value="{pk}" selected="selected">{value}</option>'.format(pk=2, value='Two')
|
||||
|
||||
assert selected_option in widget_output, widget_output
|
||||
assert selected_option2 in widget_output
|
||||
|
||||
|
||||
class TestModelSelect2Mixin(TestHeavySelect2Mixin):
|
||||
form = forms.AlbumModelSelect2WidgetForm(initial={'primary_genre': 1})
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def genres(self, db):
|
||||
return mommy.make(Genre, 100)
|
||||
|
||||
def test_selected_option(self, db, genres):
|
||||
genre = genres[0]
|
||||
genre2 = genres[1]
|
||||
not_required_field = self.form.fields['primary_genre']
|
||||
assert not_required_field.required is False
|
||||
widget_output = not_required_field.widget.render(
|
||||
'primary_genre', genre.pk)
|
||||
selected_option = '<option value="{pk}" selected="selected">{value}</option>'.format(
|
||||
pk=genre.pk, value=force_text(genre))
|
||||
unselected_option = '<option value="{pk}">{value}</option>'.format(
|
||||
pk=genre2.pk, value=force_text(genre2))
|
||||
|
||||
assert selected_option in widget_output, widget_output
|
||||
assert unselected_option not in widget_output
|
||||
|
||||
def test_get_queryset(self):
|
||||
widget = ModelSelect2Widget()
|
||||
with pytest.raises(NotImplementedError):
|
||||
widget.get_queryset()
|
||||
widget.model = Genre
|
||||
assert isinstance(widget.get_queryset(), QuerySet)
|
||||
widget.model = None
|
||||
widget.queryset = Genre.objects.all()
|
||||
assert isinstance(widget.get_queryset(), QuerySet)
|
||||
|
||||
def test_get_search_fields(self):
|
||||
widget = ModelSelect2Widget()
|
||||
with pytest.raises(NotImplementedError):
|
||||
widget.get_search_fields()
|
||||
|
||||
widget.search_fields = ['title__icontains']
|
||||
assert isinstance(widget.get_search_fields(), collections.Iterable)
|
||||
assert all(isinstance(x, text_type) for x in widget.get_search_fields())
|
||||
|
||||
def test_model_kwarg(self):
|
||||
widget = ModelSelect2Widget(model=Genre, search_fields=['title__icontains'])
|
||||
genre = Genre.objects.last()
|
||||
result = widget.filter_queryset(genre.title)
|
||||
assert result.exists()
|
||||
|
||||
def test_queryset_kwarg(self):
|
||||
widget = ModelSelect2Widget(queryset=Genre.objects, search_fields=['title__icontains'])
|
||||
genre = Genre.objects.last()
|
||||
result = widget.filter_queryset(genre.title)
|
||||
assert result.exists()
|
||||
|
||||
def test_ajax_view_registration(self, client):
|
||||
widget = ModelSelect2Widget(queryset=Genre.objects, search_fields=['title__icontains'])
|
||||
widget.render('name', 'value')
|
||||
url = reverse('django_select2-json')
|
||||
genre = Genre.objects.last()
|
||||
response = client.get(url, data=dict(field_id=signing.dumps(id(widget)), term=genre.title))
|
||||
assert response.status_code == 200, response.content
|
||||
data = json.loads(response.content.decode('utf-8'))
|
||||
assert data['results']
|
||||
assert genre.pk in [result['id'] for result in data['results']]
|
||||
|
||||
def test_render(self):
|
||||
widget = ModelSelect2Widget()
|
||||
widget.render('name', 'value')
|
||||
cached_widget = cache.get(widget._get_cache_key())
|
||||
assert isinstance(cached_widget, ModelSelect2Widget)
|
||||
|
||||
|
||||
class TestHeavySelect2TagWidget(TestHeavySelect2Mixin):
|
||||
|
||||
def test_tag_attrs(self):
|
||||
widget = ModelSelect2TagWidget(queryset=Genre.objects, search_fields=['title__icontains'])
|
||||
output = widget.render('name', 'value')
|
||||
assert 'data-minimum-input-length="1"' in output
|
||||
assert 'data-tags="true"' in output
|
||||
assert 'data-token-separators' in output
|
||||
43
tests/test_views.py
Normal file
43
tests/test_views.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import json
|
||||
|
||||
from django.core import signing
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.encoding import smart_text
|
||||
|
||||
from tests.testapp.forms import AlbumModelSelect2WidgetForm
|
||||
|
||||
|
||||
class TestAutoResponseView(object):
|
||||
def test_get(self, client, artists):
|
||||
artist = artists[0]
|
||||
form = AlbumModelSelect2WidgetForm()
|
||||
assert form.as_p()
|
||||
field_id = signing.dumps(id(form.fields['artist'].widget))
|
||||
url = reverse('django_select2-json')
|
||||
response = client.get(url, {'field_id': field_id, 'term': artist.title})
|
||||
assert response.status_code == 200
|
||||
data = json.loads(response.content.decode('utf-8'))
|
||||
assert data['results']
|
||||
assert {'id': artist.pk, 'text': smart_text(artist)} in data['results']
|
||||
|
||||
def test_no_field_id(self, client, artists):
|
||||
artist = artists[0]
|
||||
url = reverse('django_select2-json')
|
||||
response = client.get(url, {'term': artist.title})
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_wrong_field_id(self, client, artists):
|
||||
artist = artists[0]
|
||||
url = reverse('django_select2-json')
|
||||
response = client.get(url, {'field_id': 123, 'term': artist.title})
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_field_id_not_found(self, client, artists):
|
||||
artist = artists[0]
|
||||
field_id = signing.dumps(123456789)
|
||||
url = reverse('django_select2-json')
|
||||
response = client.get(url, {'field_id': field_id, 'term': artist.title})
|
||||
assert response.status_code == 404
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import json
|
||||
from collections import Iterable
|
||||
|
||||
import pytest
|
||||
from django.core import signing
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db.models import QuerySet
|
||||
from model_mommy import mommy
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
from six import text_type
|
||||
|
||||
from django_select2.cache import cache
|
||||
from django_select2.forms import ModelSelect2Widget
|
||||
from tests.testapp.models import Genre
|
||||
|
||||
|
||||
class TestWidgets(object):
|
||||
url = ""
|
||||
|
||||
def test_is_hidden_multiple(self):
|
||||
from django_select2.forms import HeavySelect2MultipleWidget
|
||||
new_widget = HeavySelect2MultipleWidget(data_url="/")
|
||||
assert new_widget.is_hidden is False
|
||||
|
||||
def test_is_hidden(self):
|
||||
from django_select2.forms import HeavySelect2Widget
|
||||
new_widget = HeavySelect2Widget(data_url="/")
|
||||
assert new_widget.is_hidden is False
|
||||
|
||||
|
||||
class TestSelect2Widget(object):
|
||||
url = reverse('select2_widget')
|
||||
|
||||
def test_selecting(self, db, client, live_server, driver):
|
||||
driver.get(live_server + self.url)
|
||||
dropdown = driver.find_element_by_css_selector('.select2-results')
|
||||
assert dropdown.is_displayed() is False
|
||||
elem = driver.find_element_by_css_selector('.select2-choice')
|
||||
elem.click()
|
||||
assert dropdown.is_displayed() is True
|
||||
with pytest.raises(NoSuchElementException):
|
||||
error = driver.find_element_by_xpath('//body[@JSError]')
|
||||
pytest.fail(error.get_attribute('JSError'))
|
||||
|
||||
|
||||
class TestHeavySelect2Widget(object):
|
||||
url = reverse('heavy_select2_widget')
|
||||
|
||||
def test_heavy_select(self, db, client, live_server, driver):
|
||||
driver.get(live_server + self.url)
|
||||
dropdown = driver.find_element_by_css_selector('.select2-results')
|
||||
assert dropdown.is_displayed() is False
|
||||
elem = driver.find_element_by_css_selector('.select2-choice')
|
||||
elem.click()
|
||||
assert dropdown.is_displayed() is True
|
||||
with pytest.raises(NoSuchElementException):
|
||||
error = driver.find_element_by_xpath('//body[@JSError]')
|
||||
pytest.fail(error.get_attribute('JSError'))
|
||||
|
||||
|
||||
class TestHeavySelect2MultipleWidget(object):
|
||||
url = reverse('heavy_select2_multiple_widget')
|
||||
|
||||
def test_heavy_select_multiple(self, db, client, live_server, driver):
|
||||
driver.get(live_server + self.url)
|
||||
dropdown = driver.find_element_by_css_selector('.select2-results')
|
||||
assert dropdown.is_displayed() is False
|
||||
elem = driver.find_element_by_css_selector('.select2-choices')
|
||||
elem.click()
|
||||
assert dropdown.is_displayed() is True
|
||||
with pytest.raises(NoSuchElementException):
|
||||
error = driver.find_element_by_xpath('//body[@JSError]')
|
||||
pytest.fail(error.get_attribute('JSError'))
|
||||
|
||||
|
||||
class TestHeavySelect2Mixin(object):
|
||||
@pytest.fixture(autouse=True)
|
||||
def genres(self, db):
|
||||
mommy.make(Genre, 100)
|
||||
|
||||
def test_get_queryset(self):
|
||||
widget = ModelSelect2Widget()
|
||||
with pytest.raises(NotImplementedError):
|
||||
widget.get_queryset()
|
||||
widget.model = Genre
|
||||
assert isinstance(widget.get_queryset(), QuerySet)
|
||||
widget.model = None
|
||||
widget.queryset = Genre.objects.all()
|
||||
assert isinstance(widget.get_queryset(), QuerySet)
|
||||
|
||||
def test_get_search_fields(self):
|
||||
widget = ModelSelect2Widget()
|
||||
with pytest.raises(NotImplementedError):
|
||||
widget.get_search_fields()
|
||||
|
||||
widget.search_fields = ['title__icontains']
|
||||
assert isinstance(widget.get_search_fields(), Iterable)
|
||||
assert all(isinstance(x, text_type) for x in widget.get_search_fields())
|
||||
|
||||
def test_model_kwarg(self):
|
||||
widget = ModelSelect2Widget(model=Genre, search_fields=['title__icontains'])
|
||||
genre = Genre.objects.last()
|
||||
result = widget.filter_queryset(genre.title)
|
||||
assert result.exists()
|
||||
|
||||
def test_queryset_kwarg(self):
|
||||
widget = ModelSelect2Widget(queryset=Genre.objects, search_fields=['title__icontains'])
|
||||
genre = Genre.objects.last()
|
||||
result = widget.filter_queryset(genre.title)
|
||||
assert result.exists()
|
||||
|
||||
def test_widget_id(self):
|
||||
widget = ModelSelect2Widget()
|
||||
widget.render('name', 'value')
|
||||
assert widget.widget_id
|
||||
assert signing.loads(widget.widget_id) == id(widget)
|
||||
|
||||
def test_render(self):
|
||||
widget = ModelSelect2Widget()
|
||||
widget.render('name', 'value')
|
||||
cached_widget = cache.get(widget._get_cache_key())
|
||||
assert isinstance(cached_widget, ModelSelect2Widget)
|
||||
assert cached_widget.widget_id == widget.widget_id
|
||||
|
||||
def test_ajax_view_registration(self, client):
|
||||
widget = ModelSelect2Widget(queryset=Genre.objects, search_fields=['title__icontains'])
|
||||
widget.render('name', 'value')
|
||||
url = reverse('django_select2_central_json')
|
||||
genre = Genre.objects.last()
|
||||
response = client.get(url, data=dict(field_id=widget.widget_id, term=genre.title))
|
||||
assert response.status_code == 200, response.content
|
||||
data = json.loads(response.content.decode('utf-8'))
|
||||
assert data['results']
|
||||
assert genre.pk in [result['id'] for result in data['results']]
|
||||
|
|
@ -4,76 +4,137 @@ from __future__ import absolute_import, unicode_literals
|
|||
from django import forms
|
||||
|
||||
from django_select2.forms import (
|
||||
HeavySelect2MultipleWidget, HeavySelect2Widget, Select2MultipleWidget,
|
||||
Select2Widget,
|
||||
ModelSelect2MultipleWidget, ModelSelect2Widget)
|
||||
HeavySelect2MultipleWidget, HeavySelect2Widget, ModelSelect2MultipleWidget,
|
||||
ModelSelect2TagWidget, ModelSelect2Widget, Select2MultipleWidget,
|
||||
Select2Widget
|
||||
)
|
||||
from tests.testapp import models
|
||||
from tests.testapp.models import Album
|
||||
|
||||
|
||||
class GenreModelForm(forms.ModelForm):
|
||||
class TitleSearchFieldMixin(object):
|
||||
search_fields = [
|
||||
'title__icontains'
|
||||
]
|
||||
|
||||
|
||||
class TitleModelSelect2Widget(TitleSearchFieldMixin, ModelSelect2Widget):
|
||||
pass
|
||||
|
||||
|
||||
class TitleModelSelect2MultipleWidget(TitleSearchFieldMixin, ModelSelect2MultipleWidget):
|
||||
pass
|
||||
|
||||
|
||||
class GenreSelect2TagWidget(TitleSearchFieldMixin, ModelSelect2TagWidget):
|
||||
model = models.Genre
|
||||
|
||||
def create_value(self, value):
|
||||
self.get_queryset().create(title=value)
|
||||
|
||||
|
||||
class AlbumSelect2WidgetForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = models.Genre
|
||||
model = models.Album
|
||||
fields = (
|
||||
'title',
|
||||
)
|
||||
|
||||
|
||||
class GenreForm(forms.Form):
|
||||
title = forms.CharField(max_length=50)
|
||||
|
||||
|
||||
class ArtistModelForm(forms.ModelForm):
|
||||
test = forms.BooleanField('asdf')
|
||||
|
||||
class Meta:
|
||||
model = models.Artist
|
||||
fields = (
|
||||
'title',
|
||||
'genres',
|
||||
'artist',
|
||||
'primary_genre',
|
||||
)
|
||||
widgets = {
|
||||
'genres': Select2MultipleWidget
|
||||
'artist': Select2Widget,
|
||||
'primary_genre': Select2Widget,
|
||||
|
||||
}
|
||||
|
||||
|
||||
class ArtistForm(forms.Form):
|
||||
class AlbumSelect2MultipleWidgetForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = models.Album
|
||||
fields = (
|
||||
'genres',
|
||||
'featured_artists',
|
||||
)
|
||||
widgets = {
|
||||
'genres': Select2MultipleWidget,
|
||||
'featured_artists': Select2MultipleWidget,
|
||||
}
|
||||
|
||||
|
||||
class AlbumModelSelect2WidgetForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = models.Album
|
||||
fields = (
|
||||
'artist',
|
||||
'primary_genre',
|
||||
)
|
||||
widgets = {
|
||||
'artist': ModelSelect2Widget(
|
||||
model=models.Artist,
|
||||
search_fields=['title__icontains']
|
||||
),
|
||||
'primary_genre': ModelSelect2Widget(
|
||||
model=models.Genre,
|
||||
search_fields=['title__icontains']
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class AlbumModelSelect2MultipleWidgetRequiredForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Album
|
||||
fields = (
|
||||
'genres',
|
||||
'featured_artists',
|
||||
)
|
||||
widgets = {
|
||||
'genres': TitleModelSelect2MultipleWidget,
|
||||
'featured_artists': TitleModelSelect2MultipleWidget,
|
||||
}
|
||||
|
||||
|
||||
class ArtistModelSelect2MultipleWidgetForm(forms.Form):
|
||||
title = forms.CharField(max_length=50)
|
||||
genres = forms.ModelMultipleChoiceField(widget=ModelSelect2MultipleWidget(
|
||||
queryset=models.Genre.objects.all(),
|
||||
search_fields=['title__icontains'],
|
||||
), queryset=models.Genre.objects.all())
|
||||
), queryset=models.Genre.objects.all(), required=False)
|
||||
|
||||
|
||||
class AlbumModelForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = models.Album
|
||||
fields = (
|
||||
'title',
|
||||
'artist',
|
||||
)
|
||||
|
||||
|
||||
class AlbumForm(forms.Form):
|
||||
title = forms.CharField(max_length=255)
|
||||
artist = forms.ModelChoiceField(widget=ModelSelect2Widget(
|
||||
model=models.Artist,
|
||||
search_fields=['title__icontains']
|
||||
), queryset=models.Artist.objects.all())
|
||||
NUMBER_CHOICES = [
|
||||
(1, 'One'),
|
||||
(2, 'Two'),
|
||||
(3, 'Three'),
|
||||
(4, 'Four'),
|
||||
]
|
||||
|
||||
|
||||
class Select2WidgetForm(forms.Form):
|
||||
NUMBER_CHOICES = [
|
||||
(1, 'One'),
|
||||
(2, 'Two'),
|
||||
(3, 'Three'),
|
||||
(4, 'Four'),
|
||||
]
|
||||
number = forms.ChoiceField(widget=Select2Widget(), choices=NUMBER_CHOICES)
|
||||
number = forms.ChoiceField(widget=Select2Widget, choices=NUMBER_CHOICES, required=False)
|
||||
|
||||
|
||||
class HeavySelect2WidgetForm(forms.Form):
|
||||
heavy_number = forms.ChoiceField(widget=HeavySelect2Widget(data_view='heavy_data'))
|
||||
artist = forms.ChoiceField(
|
||||
widget=HeavySelect2Widget(data_view='heavy_data', choices=NUMBER_CHOICES)
|
||||
)
|
||||
primary_genre = forms.ChoiceField(
|
||||
widget=HeavySelect2Widget(data_view='heavy_data', choices=NUMBER_CHOICES),
|
||||
required=False
|
||||
)
|
||||
|
||||
|
||||
class HeavySelect2MultipleWidgetForm(forms.Form):
|
||||
heavy_number = forms.MultipleChoiceField(widget=HeavySelect2MultipleWidget(data_view='heavy_data'))
|
||||
genres = forms.MultipleChoiceField(
|
||||
widget=HeavySelect2MultipleWidget(data_view='heavy_data', choices=NUMBER_CHOICES)
|
||||
)
|
||||
featured_artists = forms.MultipleChoiceField(
|
||||
widget=HeavySelect2MultipleWidget(data_view='heavy_data', choices=NUMBER_CHOICES),
|
||||
required=False
|
||||
)
|
||||
|
||||
|
||||
class ModelSelect2TagWidgetForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Album
|
||||
fields = ['genres']
|
||||
widgets = {
|
||||
'genres': GenreSelect2TagWidget
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,17 +26,9 @@ class Artist(models.Model):
|
|||
class Album(models.Model):
|
||||
title = models.CharField(max_length=255)
|
||||
artist = models.ForeignKey(Artist)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Song(models.Model):
|
||||
title = models.CharField(max_length=255)
|
||||
album = models.ForeignKey(Album, blank=True, null=True)
|
||||
artist = models.ForeignKey(Artist)
|
||||
genres = models.ManyToManyField(Genre, blank=True, null=True)
|
||||
featured_artists = models.ManyToManyField(Artist, blank=True, related_name='featured_album_set')
|
||||
primary_genre = models.ForeignKey(Genre, blank=True, null=True, related_name='primary_album_set')
|
||||
genres = models.ManyToManyField(Genre)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ DEBUG = True
|
|||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': 'testdb.sqlite',
|
||||
'NAME': ':memory:',
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,5 +48,3 @@ if os.environ.get('TRAVIS'):
|
|||
'LOCATION': 'localhost:11211',
|
||||
}
|
||||
}
|
||||
|
||||
SELECT2_BOOTSTRAP = True
|
||||
4
tests/testapp/static/jquery-2.1.4.min.js
vendored
4
tests/testapp/static/jquery-2.1.4.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -3,7 +3,11 @@
|
|||
<html>
|
||||
<head>
|
||||
{{ form.media.css }}
|
||||
<style type="text/css">select{width:200px}</style>
|
||||
<style type="text/css">
|
||||
select {
|
||||
width: 200px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<form method="post" action="">
|
||||
|
|
@ -11,7 +15,7 @@
|
|||
{{ form }}
|
||||
<input type="submit" value="Submit Form"/>
|
||||
</form>
|
||||
<script src="{% static 'jquery-2.1.4.min.js' %}"></script>
|
||||
<script src="{% static '//code.jquery.com/jquery-2.1.4.min.js' %}"></script>
|
||||
<script type="text/javascript">
|
||||
window.onerror = function (msg) {
|
||||
$("body").attr("JSError", msg);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ from __future__ import absolute_import, unicode_literals
|
|||
from django.conf.urls import include, patterns, url
|
||||
|
||||
from .forms import (
|
||||
ArtistForm, HeavySelect2MultipleWidgetForm, HeavySelect2WidgetForm,
|
||||
Select2WidgetForm
|
||||
AlbumModelSelect2WidgetForm, HeavySelect2MultipleWidgetForm,
|
||||
HeavySelect2WidgetForm, ModelSelect2TagWidgetForm, Select2WidgetForm
|
||||
)
|
||||
from .views import TemplateFormView, heavy_data
|
||||
|
||||
|
|
@ -16,11 +16,18 @@ urlpatterns = patterns(
|
|||
url(r'^heavy_select2_widget/$',
|
||||
TemplateFormView.as_view(form_class=HeavySelect2WidgetForm), name='heavy_select2_widget'),
|
||||
url(r'^heavy_select2_multiple_widget/$',
|
||||
TemplateFormView.as_view(form_class=HeavySelect2MultipleWidgetForm), name='heavy_select2_multiple_widget'),
|
||||
url(r'^single_value_model_field/$',
|
||||
TemplateFormView.as_view(form_class=ArtistForm), name='single_value_model_field'),
|
||||
url(r'^heavy_data/$',
|
||||
heavy_data, name='heavy_data'),
|
||||
TemplateFormView.as_view(form_class=HeavySelect2MultipleWidgetForm),
|
||||
name='heavy_select2_multiple_widget'),
|
||||
|
||||
url(r'^model_select2_widget/$',
|
||||
TemplateFormView.as_view(form_class=AlbumModelSelect2WidgetForm),
|
||||
name='model_select2_widget'),
|
||||
|
||||
url(r'^model_select2_tag_widget/$',
|
||||
TemplateFormView.as_view(form_class=ModelSelect2TagWidgetForm),
|
||||
name='model_select2_tag_widget'),
|
||||
|
||||
url(r'^heavy_data/$', heavy_data, name='heavy_data'),
|
||||
|
||||
url(r'^select2/', include('django_select2.urls')),
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue