9 Commits
0.7.6 ... 0.8

Author SHA1 Message Date
Florent Lebreton
6da81f2115 Release 0.8 2014-12-04 13:58:40 +01:00
Florent Lebreton
d12b1e80ac Merge pull request #5 from Gagaro/master
Python3 / Django1.7 support & better testing
2014-12-04 13:50:38 +01:00
Gagaro
880033d3a5 fix: django.utils.six not available before django 1.6 2014-12-04 12:34:49 +01:00
Gagaro
c15074873b fix: python 3 compatibility 2014-12-04 12:27:25 +01:00
Gagaro
8e912f9738 tests: improve testing by splitting tests + use six for basestring python3 compatibility 2014-12-04 12:18:53 +01:00
Gagaro
70b4f2e9b6 fix: .travis.yml matrix exclude 2014-12-04 10:31:30 +01:00
Gagaro
124fe21d06 tests: upgrade quicktest.py + improve travis configuration 2014-12-04 10:22:31 +01:00
Florent Lebreton
738eb869fc Merge pull request #4 from Gagaro/master
splitted widgets tests
2014-12-03 12:45:50 +01:00
Gagaro
32566873a4 splitted widgets tests 2014-12-03 11:18:28 +01:00
12 changed files with 150 additions and 52 deletions

View File

@@ -1,19 +1,42 @@
language: python language: python
python: python:
- "2.7" - 2.6
- 2.7
- 3.2
- 3.3
- 3.4
env: env:
- DJANGO_VERSION=1.4 - DJANGO_VERSION=1.4 MODULE=jsignature
- DJANGO_VERSION=1.5 - DJANGO_VERSION=1.5 MODULE=jsignature
- DJANGO_VERSION=1.6 MODULE=jsignature.tests
- DJANGO_VERSION=1.7 MODULE=jsignature.tests
install: install:
- pip install -r requirements.txt --use-mirrors - pip install -r requirements.txt --use-mirrors
- pip install -q Django==$DJANGO_VERSION --use-mirrors - pip install -q Django==$DJANGO_VERSION --use-mirrors
- pip install coverage - pip install coverage
script: coverage run quicktest.py jsignature script: coverage run quicktest.py $MODULE
after_success: after_success:
- pip install coveralls - pip install coveralls
- coveralls - coveralls
# We need to exclude old versions of Django for tests with python 3.
# We need to exclude old versions of Python for tests with Django >= 1.7.
matrix:
exclude:
- python: 3.2
env: DJANGO_VERSION=1.4 MODULE=jsignature
- python: 3.3
env: DJANGO_VERSION=1.4 MODULE=jsignature
- python: 3.4
env: DJANGO_VERSION=1.4 MODULE=jsignature
- python: 3.4
env: DJANGO_VERSION=1.5 MODULE=jsignature
- python: 3.4
env: DJANGO_VERSION=1.6 MODULE=jsignature.tests
- python: 2.6
env: DJANGO_VERSION=1.7 MODULE=jsignature.tests

View File

@@ -2,6 +2,15 @@
CHANGELOG CHANGELOG
========= =========
0.8 (2014-12-04)
==================
** New **
- Add support for Python 3 (@Gagaro)
- Add support for Django 1.7 (@Gagaro)
0.7.6 (2014-11-26) 0.7.6 (2014-11-26)
================== ==================

View File

@@ -3,19 +3,21 @@
with jSignature jQuery plugin with jSignature jQuery plugin
""" """
import json import json
import six
from django.db import models from django.db import models
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from .forms import ( from .forms import (
JSignatureField as JSignatureFormField, JSignatureField as JSignatureFormField,
JSIGNATURE_EMPTY_VALUES) JSIGNATURE_EMPTY_VALUES)
class JSignatureField(models.Field): class JSignatureField(six.with_metaclass(models.SubfieldBase, models.Field)):
""" """
A model field handling a signature captured with jSignature A model field handling a signature captured with jSignature
""" """
description = "A signature captured with jSignature" description = "A signature captured with jSignature"
__metaclass__ = models.SubfieldBase
def get_internal_type(self): def get_internal_type(self):
return 'TextField' return 'TextField'
@@ -37,7 +39,7 @@ class JSignatureField(models.Field):
def get_prep_value(self, value): def get_prep_value(self, value):
if value in JSIGNATURE_EMPTY_VALUES: if value in JSIGNATURE_EMPTY_VALUES:
return None return None
elif isinstance(value, basestring): elif isinstance(value, six.string_types):
return value return value
elif isinstance(value, list): elif isinstance(value, list):
return json.dumps(value) return json.dumps(value)

View File

@@ -1,4 +1,6 @@
import json import json
import six
from django.test import SimpleTestCase from django.test import SimpleTestCase
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@@ -8,35 +10,49 @@ from ..forms import JSignatureField as JSignatureFormField
class JSignatureFieldTest(SimpleTestCase): class JSignatureFieldTest(SimpleTestCase):
def test_to_python(self): def test_to_python_empty(self):
f = JSignatureField() f = JSignatureField()
# Empty values
for val in ['', [], '[]']: for val in ['', [], '[]']:
self.assertIsNone(f.to_python(val)) self.assertIsNone(f.to_python(val))
# Correct values
def test_to_python_correct_value_python(self):
f = JSignatureField()
val = [{"x": [1, 2], "y": [3, 4]}] val = [{"x": [1, 2], "y": [3, 4]}]
self.assertEquals(val, f.to_python(val)) self.assertEquals(val, f.to_python(val))
def test_to_python_correct_value_json(self):
f = JSignatureField()
val = [{"x": [1, 2], "y": [3, 4]}]
val_str = '[{"x":[1,2], "y":[3,4]}]' val_str = '[{"x":[1,2], "y":[3,4]}]'
self.assertEquals(val, f.to_python(val_str)) self.assertEquals(val, f.to_python(val_str))
# Incorrect values
def test_to_python_incorrect_value(self):
f = JSignatureField()
val = 'foo' val = 'foo'
self.assertRaises(ValidationError, f.to_python, val) self.assertRaises(ValidationError, f.to_python, val)
def test_get_prep_value(self): def test_get_prep_value_empty(self):
f = JSignatureField() f = JSignatureField()
# Empty values
for val in ['', [], '[]']: for val in ['', [], '[]']:
self.assertIsNone(f.get_prep_value(val)) self.assertIsNone(f.get_prep_value(val))
# Correct values
def test_get_prep_value_correct_values_python(self):
f = JSignatureField()
val = [{"x": [1, 2], "y": [3, 4]}] val = [{"x": [1, 2], "y": [3, 4]}]
val_prep = f.get_prep_value(val) val_prep = f.get_prep_value(val)
self.assertIsInstance(val_prep, basestring) self.assertIsInstance(val_prep, six.string_types)
self.assertEquals(val, json.loads(val_prep)) self.assertEquals(val, json.loads(val_prep))
def test_get_prep_value_correct_values_json(self):
f = JSignatureField()
val = [{"x": [1, 2], "y": [3, 4]}]
val_str = '[{"x":[1,2], "y":[3,4]}]' val_str = '[{"x":[1,2], "y":[3,4]}]'
val_prep = f.get_prep_value(val_str) val_prep = f.get_prep_value(val_str)
self.assertIsInstance(val_prep, basestring) self.assertIsInstance(val_prep, six.string_types)
self.assertEquals(val, json.loads(val_prep)) self.assertEquals(val, json.loads(val_prep))
# Incorrect values
def test_get_prep_value_incorrect_values(self):
f = JSignatureField()
val = type('Foo') val = type('Foo')
self.assertRaises(ValidationError, f.get_prep_value, val) self.assertRaises(ValidationError, f.get_prep_value, val)

View File

@@ -11,14 +11,17 @@ class JSignatureFormFieldTest(SimpleTestCase):
f = JSignatureField() f = JSignatureField()
self.assertIsInstance(f.widget, JSignatureWidget) self.assertIsInstance(f.widget, JSignatureWidget)
def test_to_python(self): def test_to_python_empty_values(self):
f = JSignatureField() f = JSignatureField()
# Empty values
for val in ['', [], '[]']: for val in ['', [], '[]']:
self.assertIsNone(f.to_python(val)) self.assertIsNone(f.to_python(val))
# Correct values
def test_to_python_correct_values(self):
f = JSignatureField()
val = '[{"x":[1,2], "y":[3,4]}]' val = '[{"x":[1,2], "y":[3,4]}]'
self.assertEquals([{'x': [1, 2], 'y': [3, 4]}], f.to_python(val)) self.assertEquals([{'x': [1, 2], 'y': [3, 4]}], f.to_python(val))
# Incorrect values
def test_to_python_incorrect_values(self):
f = JSignatureField()
val = 'foo' val = 'foo'
self.assertRaises(ValidationError, f.to_python, val) self.assertRaises(ValidationError, f.to_python, val)

View File

@@ -19,7 +19,7 @@ class JSignatureFieldsMixinTest(SimpleTestCase):
def tearDown(self): def tearDown(self):
settings.INSTALLED_APPS = self.old_installed_apps settings.INSTALLED_APPS = self.old_installed_apps
def test_save(self): def test_save_create(self):
# If an object is created signed, signature date must be set # If an object is created signed, signature date must be set
signature_value = [{"x": [1, 2], "y": [3, 4]}] signature_value = [{"x": [1, 2], "y": [3, 4]}]
i = JSignatureTestModel(signature=signature_value) i = JSignatureTestModel(signature=signature_value)
@@ -27,16 +27,19 @@ class JSignatureFieldsMixinTest(SimpleTestCase):
i = JSignatureTestModel.objects.get(pk=i.pk) i = JSignatureTestModel.objects.get(pk=i.pk)
self.assertEqual(date.today(), i.signature_date.date()) self.assertEqual(date.today(), i.signature_date.date())
def test_save_no_change(self):
# If signature doesn't change, signature date must not be updated # If signature doesn't change, signature date must not be updated
signature_value = [{"x": [1, 2], "y": [3, 4]}]
i = JSignatureTestModel(signature=signature_value) i = JSignatureTestModel(signature=signature_value)
i.save() i.save()
i.signature_date = date(2013, 1, 1) i.signature_date = date(2013, 1, 1)
i.signature = signature_value
i.save() i.save()
i = JSignatureTestModel.objects.get(pk=i.pk) i = JSignatureTestModel.objects.get(pk=i.pk)
self.assertEqual(date(2013, 1, 1), i.signature_date.date()) self.assertEqual(date(2013, 1, 1), i.signature_date.date())
def test_save_change(self):
# If signature changes, signature date must be updated too # If signature changes, signature date must be updated too
signature_value = [{"x": [1, 2], "y": [3, 4]}]
new_signature_value = [{"x": [5, 6], "y": [7, 8]}] new_signature_value = [{"x": [5, 6], "y": [7, 8]}]
i = JSignatureTestModel(signature=signature_value, i = JSignatureTestModel(signature=signature_value,
signature_date=date(2013, 1, 1)) signature_date=date(2013, 1, 1))
@@ -47,7 +50,9 @@ class JSignatureFieldsMixinTest(SimpleTestCase):
i = JSignatureTestModel.objects.get(pk=i.pk) i = JSignatureTestModel.objects.get(pk=i.pk)
self.assertEqual(date.today(), i.signature_date.date()) self.assertEqual(date.today(), i.signature_date.date())
def test_save_none(self):
# If sinature is set to None, it must be the same for signature_date # If sinature is set to None, it must be the same for signature_date
signature_value = [{"x": [1, 2], "y": [3, 4]}]
i = JSignatureTestModel(signature=signature_value) i = JSignatureTestModel(signature=signature_value)
i.save() i.save()
i.signature = None i.signature = None

View File

@@ -13,22 +13,24 @@ DUMMY_STR_VALUE = json.dumps(DUMMY_VALUE)
class UtilsTest(SimpleTestCase): class UtilsTest(SimpleTestCase):
def test_inputs(self): def test_inputs_bad_str_value(self):
# Bad str value
self.assertRaises(ValueError, draw_signature, 'foo_bar') self.assertRaises(ValueError, draw_signature, 'foo_bar')
# Bad type value
def test_inputs_bad_type_value(self):
self.assertRaises(ValueError, draw_signature, object()) self.assertRaises(ValueError, draw_signature, object())
# Good list value
def test_inputs_good_list_value(self):
draw_signature(DUMMY_VALUE) draw_signature(DUMMY_VALUE)
# Good str value
def test_inputs_good_str_value(self):
draw_signature(DUMMY_STR_VALUE) draw_signature(DUMMY_STR_VALUE)
def test_outputs(self): def test_outputs_as_file(self):
# As a file
output = draw_signature(DUMMY_VALUE, as_file=True) output = draw_signature(DUMMY_VALUE, as_file=True)
self.assertTrue(os.path.isfile(output)) self.assertTrue(os.path.isfile(output))
self.assertIsNotNone(imghdr.what(output)) self.assertIsNotNone(imghdr.what(output))
# As an Image
def test_outputs_as_image(self):
output = draw_signature(DUMMY_VALUE) output = draw_signature(DUMMY_VALUE)
self.assertTrue(issubclass(output.__class__, Image.Image)) self.assertTrue(issubclass(output.__class__, Image.Image))
self.assertTrue(all(output.getbbox())) self.assertTrue(all(output.getbbox()))

View File

@@ -1,5 +1,6 @@
import json import json
from pyquery import PyQuery as pq from pyquery import PyQuery as pq
import six
from django.test import SimpleTestCase from django.test import SimpleTestCase
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@@ -39,21 +40,28 @@ class JSignatureWidgetTest(SimpleTestCase):
self.assertEqual(400, config.get('width')) self.assertEqual(400, config.get('width'))
self.assertEqual(JSIGNATURE_HEIGHT, config.get('height')) self.assertEqual(JSIGNATURE_HEIGHT, config.get('height'))
def test_prep_value(self): def test_prep_value_empty_values(self):
w = JSignatureWidget() w = JSignatureWidget()
# Empty values
for val in ['', [], '[]']: for val in ['', [], '[]']:
self.assertEqual('[]', w.prep_value(val)) self.assertEqual('[]', w.prep_value(val))
# Correct values
def test_prep_value_correct_values_python(self):
w = JSignatureWidget()
val = [{"x": [1, 2], "y": [3, 4]}] val = [{"x": [1, 2], "y": [3, 4]}]
val_prep = w.prep_value(val) val_prep = w.prep_value(val)
self.assertIsInstance(val_prep, basestring) self.assertIsInstance(val_prep, six.string_types)
self.assertEquals(val, json.loads(val_prep)) self.assertEquals(val, json.loads(val_prep))
def test_prep_value_correct_values_json(self):
w = JSignatureWidget()
val = [{"x": [1, 2], "y": [3, 4]}]
val_str = '[{"x":[1,2], "y":[3,4]}]' val_str = '[{"x":[1,2], "y":[3,4]}]'
val_prep = w.prep_value(val_str) val_prep = w.prep_value(val_str)
self.assertIsInstance(val_prep, basestring) self.assertIsInstance(val_prep, six.string_types)
self.assertEquals(val, json.loads(val_prep)) self.assertEquals(val, json.loads(val_prep))
# Incorrect values
def test_prep_value_incorrect_values(self):
w = JSignatureWidget()
val = type('Foo') val = type('Foo')
self.assertRaises(ValidationError, w.prep_value, val) self.assertRaises(ValidationError, w.prep_value, val)
@@ -64,11 +72,12 @@ class JSignatureWidgetTest(SimpleTestCase):
self.assertEqual(1, len(pq('.jsign-wrapper', output))) self.assertEqual(1, len(pq('.jsign-wrapper', output)))
self.assertEqual(1, len(pq('[type=hidden]', output))) self.assertEqual(1, len(pq('[type=hidden]', output)))
def test_render_reset_button(self): def test_render_reset_button_true(self):
w = JSignatureWidget(jsignature_attrs={'ResetButton': True}) w = JSignatureWidget(jsignature_attrs={'ResetButton': True})
output = w.render(name='foo', value=None) output = w.render(name='foo', value=None)
self.assertEqual(1, len(pq('[type=button]', output))) self.assertEqual(1, len(pq('[type=button]', output)))
def test_render_reset_button_false(self):
w = JSignatureWidget(jsignature_attrs={'ResetButton': False}) w = JSignatureWidget(jsignature_attrs={'ResetButton': False})
output = w.render(name='foo', value=None) output = w.render(name='foo', value=None)
self.assertEqual(0, len(pq('[type=button]', output))) self.assertEqual(0, len(pq('[type=button]', output)))

View File

@@ -3,11 +3,13 @@
with jSignature jQuery plugin with jSignature jQuery plugin
""" """
import json import json
import six
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.forms.widgets import HiddenInput from django.forms.widgets import HiddenInput
from django.core import validators from django.core import validators
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from jsignature.settings import JSIGNATURE_DEFAULT_CONFIG from jsignature.settings import JSIGNATURE_DEFAULT_CONFIG
@@ -51,7 +53,7 @@ class JSignatureWidget(HiddenInput):
""" Prepare value before effectively render widget """ """ Prepare value before effectively render widget """
if value in JSIGNATURE_EMPTY_VALUES: if value in JSIGNATURE_EMPTY_VALUES:
return "[]" return "[]"
elif isinstance(value, basestring): elif isinstance(value, six.string_types):
return value return value
elif isinstance(value, list): elif isinstance(value, list):
return json.dumps(value) return json.dumps(value)

View File

@@ -3,6 +3,7 @@ import sys
import argparse import argparse
from django.conf import settings from django.conf import settings
class QuickDjangoTest(object): class QuickDjangoTest(object):
""" """
A quick way to run the Django test suite without a fully-configured project. A quick way to run the Django test suite without a fully-configured project.
@@ -16,8 +17,6 @@ class QuickDjangoTest(object):
""" """
DIRNAME = os.path.dirname(__file__) DIRNAME = os.path.dirname(__file__)
INSTALLED_APPS = ( INSTALLED_APPS = (
'django.contrib.contenttypes',
'django.contrib.sessions',
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -29,7 +28,10 @@ class QuickDjangoTest(object):
Fire up the Django test suite developed for version 1.2 Fire up the Django test suite developed for version 1.2
""" """
settings.configure( settings.configure(
DATABASES={ TEMPLATE_DIRS = ('jsignature/templates/',),
ROOT_URLCONF = 'jsignature.tests',
DEBUG = True,
DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(self.DIRNAME, 'database.db'), 'NAME': os.path.join(self.DIRNAME, 'database.db'),
@@ -39,11 +41,27 @@ class QuickDjangoTest(object):
'PORT': '', 'PORT': '',
} }
}, },
INSTALLED_APPS=self.INSTALLED_APPS + self.apps, MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
),
INSTALLED_APPS = self.INSTALLED_APPS + self.apps
) )
from django.test.simple import DjangoTestSuiteRunner # Setup is needed for Django >= 1.7
failures = DjangoTestSuiteRunner().run_tests(self.apps, verbosity=1) import django
if failures: # pragma: no cover if hasattr(django, 'setup'):
django.setup()
try:
from django.test.runner import DiscoverRunner
failures = DiscoverRunner().run_tests(self.apps, verbosity=1)
except ImportError:
# DjangoTestSuiteRunner has been deprecated in Django 1.7
from django.test.simple import DjangoTestSuiteRunner
failures = DjangoTestSuiteRunner().run_tests(self.apps, verbosity=1)
if failures: # pragma: no cover
sys.exit(failures) sys.exit(failures)
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -1,2 +1,3 @@
pillow pillow
pyquery pyquery
six

View File

@@ -5,14 +5,14 @@ here = os.path.abspath(os.path.dirname(__file__))
setup( setup(
name='django-jsignature', name='django-jsignature',
version='0.7.6', version='0.8',
author='Florent Lebreton', author='Florent Lebreton',
author_email='florent.lebreton@makina-corpus.com', author_email='florent.lebreton@makina-corpus.com',
url='https://github.com/fle/django-jsignature', url='https://github.com/fle/django-jsignature',
download_url="https://github.com/fle/django-jsignature/tarball/0.7.6", download_url='https://github.com/fle/django-jsignature/tarball/0.8',
description="Use jSignature jQuery plugin in your django projects", description='Use jSignature jQuery plugin in your django projects',
long_description=open(os.path.join(here, 'README.rst')).read() + '\n\n' + long_description=open(os.path.join(here, 'README.rst')).read() + '\n\n' +
open(os.path.join(here, 'CHANGES')).read(), open(os.path.join(here, 'CHANGES')).read(),
license='LPGL, see LICENSE file.', license='LPGL, see LICENSE file.',
install_requires=['Django'], install_requires=['Django'],
packages=find_packages(), packages=find_packages(),
@@ -26,5 +26,13 @@ setup(
'Environment :: Web Environment', 'Environment :: Web Environment',
'Framework :: Django', 'Framework :: Django',
'Development Status :: 5 - Production/Stable', 'Development Status :: 5 - Production/Stable',
'Programming Language :: Python :: 2.7'], 'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4'
],
) )