13
.travis.yml
Normal file
13
.travis.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
language: python
|
||||||
|
install:
|
||||||
|
- pip install tox coveralls
|
||||||
|
script:
|
||||||
|
- tox
|
||||||
|
env:
|
||||||
|
- TOXENV=py27-1.7
|
||||||
|
- TOXENV=py32-1.7
|
||||||
|
- TOXENV=py33-1.7
|
||||||
|
- TOXENV=py27-master
|
||||||
|
- TOXENV=py32-master
|
||||||
|
- TOXENV=py33-master
|
||||||
|
after_success: coveralls
|
||||||
@@ -1 +1,3 @@
|
|||||||
default_app_config = 'django.contrib.formtools.apps.FormToolsConfig'
|
__version__ = '1.0'
|
||||||
|
|
||||||
|
default_app_config = 'formtools.apps.FormToolsConfig'
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class FormToolsConfig(AppConfig):
|
class FormToolsConfig(AppConfig):
|
||||||
name = 'django.contrib.formtools'
|
name = 'formtools'
|
||||||
verbose_name = _("Form Tools")
|
verbose_name = _("Form Tools")
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
"""
|
"""
|
||||||
Formtools Preview application.
|
Formtools Preview application.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
from django.template.context import RequestContext
|
from django.template.context import RequestContext
|
||||||
from django.utils.crypto import constant_time_compare
|
from django.utils.crypto import constant_time_compare
|
||||||
from django.contrib.formtools.utils import form_hmac
|
|
||||||
|
from .utils import form_hmac
|
||||||
|
|
||||||
AUTO_ID = 'formtools_%s' # Each form here uses this as its auto_id parameter.
|
AUTO_ID = 'formtools_%s' # Each form here uses this as its auto_id parameter.
|
||||||
|
|
||||||
@@ -22,7 +22,10 @@ class FormPreview(object):
|
|||||||
self.form, self.state = form, {}
|
self.form, self.state = form, {}
|
||||||
|
|
||||||
def __call__(self, request, *args, **kwargs):
|
def __call__(self, request, *args, **kwargs):
|
||||||
stage = {'1': 'preview', '2': 'post'}.get(request.POST.get(self.unused_name('stage')), 'preview')
|
stage = {
|
||||||
|
'1': 'preview',
|
||||||
|
'2': 'post',
|
||||||
|
}.get(request.POST.get(self.unused_name('stage')), 'preview')
|
||||||
self.parse_params(*args, **kwargs)
|
self.parse_params(*args, **kwargs)
|
||||||
try:
|
try:
|
||||||
method = getattr(self, stage + '_' + request.method.lower())
|
method = getattr(self, stage + '_' + request.method.lower())
|
||||||
@@ -48,38 +51,47 @@ class FormPreview(object):
|
|||||||
|
|
||||||
def preview_get(self, request):
|
def preview_get(self, request):
|
||||||
"Displays the form"
|
"Displays the form"
|
||||||
f = self.form(auto_id=self.get_auto_id(), initial=self.get_initial(request))
|
f = self.form(auto_id=self.get_auto_id(),
|
||||||
|
initial=self.get_initial(request))
|
||||||
return render_to_response(self.form_template,
|
return render_to_response(self.form_template,
|
||||||
self.get_context(request, f),
|
self.get_context(request, f),
|
||||||
context_instance=RequestContext(request))
|
context_instance=RequestContext(request))
|
||||||
|
|
||||||
def preview_post(self, request):
|
def preview_post(self, request):
|
||||||
"Validates the POST data. If valid, displays the preview page. Else, redisplays form."
|
"""
|
||||||
|
Validates the POST data. If valid, displays the preview page.
|
||||||
|
Else, redisplays form.
|
||||||
|
"""
|
||||||
f = self.form(request.POST, auto_id=self.get_auto_id())
|
f = self.form(request.POST, auto_id=self.get_auto_id())
|
||||||
context = self.get_context(request, f)
|
context = self.get_context(request, f)
|
||||||
if f.is_valid():
|
if f.is_valid():
|
||||||
self.process_preview(request, f, context)
|
self.process_preview(request, f, context)
|
||||||
context['hash_field'] = self.unused_name('hash')
|
context['hash_field'] = self.unused_name('hash')
|
||||||
context['hash_value'] = self.security_hash(request, f)
|
context['hash_value'] = self.security_hash(request, f)
|
||||||
return render_to_response(self.preview_template, context, context_instance=RequestContext(request))
|
return render_to_response(self.preview_template, context,
|
||||||
|
context_instance=RequestContext(request))
|
||||||
else:
|
else:
|
||||||
return render_to_response(self.form_template, context, context_instance=RequestContext(request))
|
return render_to_response(self.form_template, context,
|
||||||
|
context_instance=RequestContext(request))
|
||||||
|
|
||||||
def _check_security_hash(self, token, request, form):
|
def _check_security_hash(self, token, request, form):
|
||||||
expected = self.security_hash(request, form)
|
expected = self.security_hash(request, form)
|
||||||
return constant_time_compare(token, expected)
|
return constant_time_compare(token, expected)
|
||||||
|
|
||||||
def post_post(self, request):
|
def post_post(self, request):
|
||||||
"Validates the POST data. If valid, calls done(). Else, redisplays form."
|
"""
|
||||||
f = self.form(request.POST, auto_id=self.get_auto_id())
|
Validates the POST data. If valid, calls done(). Else, redisplays form.
|
||||||
if f.is_valid():
|
"""
|
||||||
if not self._check_security_hash(request.POST.get(self.unused_name('hash'), ''),
|
form = self.form(request.POST, auto_id=self.get_auto_id())
|
||||||
request, f):
|
if form.is_valid():
|
||||||
|
if not self._check_security_hash(
|
||||||
|
request.POST.get(self.unused_name('hash'), ''),
|
||||||
|
request, form):
|
||||||
return self.failed_hash(request) # Security hash failed.
|
return self.failed_hash(request) # Security hash failed.
|
||||||
return self.done(request, f.cleaned_data)
|
return self.done(request, form.cleaned_data)
|
||||||
else:
|
else:
|
||||||
return render_to_response(self.form_template,
|
return render_to_response(self.form_template,
|
||||||
self.get_context(request, f),
|
self.get_context(request, form),
|
||||||
context_instance=RequestContext(request))
|
context_instance=RequestContext(request))
|
||||||
|
|
||||||
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
|
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
|
||||||
@@ -100,7 +112,11 @@ class FormPreview(object):
|
|||||||
|
|
||||||
def get_context(self, request, form):
|
def get_context(self, request, form):
|
||||||
"Context for template rendering."
|
"Context for template rendering."
|
||||||
return {'form': form, 'stage_field': self.unused_name('stage'), 'state': self.state}
|
return {
|
||||||
|
'form': form,
|
||||||
|
'stage_field': self.unused_name('stage'),
|
||||||
|
'state': self.state,
|
||||||
|
}
|
||||||
|
|
||||||
def parse_params(self, *args, **kwargs):
|
def parse_params(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
@@ -127,7 +143,8 @@ class FormPreview(object):
|
|||||||
|
|
||||||
def security_hash(self, request, form):
|
def security_hash(self, request, form):
|
||||||
"""
|
"""
|
||||||
Calculates the security hash for the given HttpRequest and Form instances.
|
Calculates the security hash for the given HttpRequest and Form
|
||||||
|
instances.
|
||||||
|
|
||||||
Subclasses may want to take into account request-specific information,
|
Subclasses may want to take into account request-specific information,
|
||||||
such as the IP address.
|
such as the IP address.
|
||||||
@@ -145,4 +162,5 @@ class FormPreview(object):
|
|||||||
Does something with the cleaned_data and returns an
|
Does something with the cleaned_data and returns an
|
||||||
HttpResponseRedirect.
|
HttpResponseRedirect.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('You must define a done() method on your %s subclass.' % self.__class__.__name__)
|
raise NotImplementedError('You must define a done() method on your '
|
||||||
|
'%s subclass.' % self.__class__.__name__)
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
from django.contrib.formtools.wizard.storage import get_storage, MissingStorage
|
|
||||||
from django.contrib.formtools.wizard.storage.base import BaseStorage
|
|
||||||
|
|
||||||
|
|
||||||
class TestLoadStorage(TestCase):
|
|
||||||
def test_load_storage(self):
|
|
||||||
self.assertEqual(
|
|
||||||
type(get_storage('django.contrib.formtools.wizard.storage.base.BaseStorage', 'wizard1')),
|
|
||||||
BaseStorage)
|
|
||||||
|
|
||||||
def test_missing_storage(self):
|
|
||||||
self.assertRaises(MissingStorage, get_storage,
|
|
||||||
'django.contrib.formtools.wizard.storage.idontexist.IDontExistStorage', 'wizard1')
|
|
||||||
self.assertRaises(MissingStorage, get_storage,
|
|
||||||
'django.contrib.formtools.wizard.storage.base.IDontExistStorage', 'wizard1')
|
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
from django.utils.module_loading import import_string
|
from django.utils.module_loading import import_string
|
||||||
|
|
||||||
from django.contrib.formtools.wizard.storage.base import BaseStorage
|
from .base import BaseStorage
|
||||||
from django.contrib.formtools.wizard.storage.exceptions import (
|
from .exceptions import MissingStorage, NoFileStorageConfigured
|
||||||
MissingStorage, NoFileStorageConfigured)
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"BaseStorage", "MissingStorage", "NoFileStorageConfigured", "get_storage",
|
"BaseStorage", "MissingStorage", "NoFileStorageConfigured", "get_storage",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from django.utils.datastructures import MultiValueDict
|
|||||||
from django.utils.functional import lazy_property
|
from django.utils.functional import lazy_property
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
from django.contrib.formtools.wizard.storage.exceptions import NoFileStorageConfigured
|
from .exceptions import NoFileStorageConfigured
|
||||||
|
|
||||||
|
|
||||||
class BaseStorage(object):
|
class BaseStorage(object):
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from django.contrib.formtools.wizard import storage
|
from .base import BaseStorage
|
||||||
|
|
||||||
|
|
||||||
class CookieStorage(storage.BaseStorage):
|
class CookieStorage(BaseStorage):
|
||||||
encoder = json.JSONEncoder(separators=(',', ':'))
|
encoder = json.JSONEncoder(separators=(',', ':'))
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@@ -21,6 +21,7 @@ class CookieStorage(storage.BaseStorage):
|
|||||||
def update_response(self, response):
|
def update_response(self, response):
|
||||||
super(CookieStorage, self).update_response(response)
|
super(CookieStorage, self).update_response(response)
|
||||||
if self.data:
|
if self.data:
|
||||||
response.set_signed_cookie(self.prefix, self.encoder.encode(self.data))
|
response.set_signed_cookie(self.prefix,
|
||||||
|
self.encoder.encode(self.data))
|
||||||
else:
|
else:
|
||||||
response.delete_cookie(self.prefix)
|
response.delete_cookie(self.prefix)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from django.contrib.formtools.wizard import storage
|
from .base import BaseStorage
|
||||||
|
|
||||||
|
|
||||||
class SessionStorage(storage.BaseStorage):
|
class SessionStorage(BaseStorage):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(SessionStorage, self).__init__(*args, **kwargs)
|
super(SessionStorage, self).__init__(*args, **kwargs)
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ from django.utils.decorators import classonlymethod
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
from django.contrib.formtools.wizard.storage import get_storage
|
from .storage import get_storage
|
||||||
from django.contrib.formtools.wizard.storage.exceptions import NoFileStorageConfigured
|
from .storage.exceptions import NoFileStorageConfigured
|
||||||
from django.contrib.formtools.wizard.forms import ManagementForm
|
from .forms import ManagementForm
|
||||||
|
|
||||||
|
|
||||||
def normalize_name(name):
|
def normalize_name(name):
|
||||||
@@ -125,7 +125,7 @@ class WizardView(TemplateView):
|
|||||||
def get_initkwargs(cls, form_list=None, initial_dict=None,
|
def get_initkwargs(cls, form_list=None, initial_dict=None,
|
||||||
instance_dict=None, condition_dict=None, *args, **kwargs):
|
instance_dict=None, condition_dict=None, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Creates a dict with all needed parameters for the form wizard instances.
|
Creates a dict with all needed parameters for the form wizard instances
|
||||||
|
|
||||||
* `form_list` - is a list of forms. The list entries can be single form
|
* `form_list` - is a list of forms. The list entries can be single form
|
||||||
classes or tuples of (`step_name`, `form_class`). If you pass a list
|
classes or tuples of (`step_name`, `form_class`). If you pass a list
|
||||||
@@ -246,7 +246,7 @@ class WizardView(TemplateView):
|
|||||||
|
|
||||||
If a GET request reaches this point, the wizard assumes that the user
|
If a GET request reaches this point, the wizard assumes that the user
|
||||||
just starts at the first step or wants to restart the process.
|
just starts at the first step or wants to restart the process.
|
||||||
The data of the wizard will be resetted before rendering the first step.
|
The data of the wizard will be resetted before rendering the first step
|
||||||
"""
|
"""
|
||||||
self.storage.reset()
|
self.storage.reset()
|
||||||
|
|
||||||
@@ -289,8 +289,10 @@ class WizardView(TemplateView):
|
|||||||
# and try to validate
|
# and try to validate
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
# if the form is valid, store the cleaned data and files.
|
# if the form is valid, store the cleaned data and files.
|
||||||
self.storage.set_step_data(self.steps.current, self.process_step(form))
|
self.storage.set_step_data(self.steps.current,
|
||||||
self.storage.set_step_files(self.steps.current, self.process_step_files(form))
|
self.process_step(form))
|
||||||
|
self.storage.set_step_files(self.steps.current,
|
||||||
|
self.process_step_files(form))
|
||||||
|
|
||||||
# check if the current step is the last step
|
# check if the current step is the last step
|
||||||
if self.steps.current == self.steps.last:
|
if self.steps.current == self.steps.last:
|
||||||
@@ -342,13 +344,17 @@ class WizardView(TemplateView):
|
|||||||
data=self.storage.get_step_data(form_key),
|
data=self.storage.get_step_data(form_key),
|
||||||
files=self.storage.get_step_files(form_key))
|
files=self.storage.get_step_files(form_key))
|
||||||
if not form_obj.is_valid():
|
if not form_obj.is_valid():
|
||||||
return self.render_revalidation_failure(form_key, form_obj, **kwargs)
|
return self.render_revalidation_failure(form_key,
|
||||||
|
form_obj,
|
||||||
|
**kwargs)
|
||||||
final_forms[form_key] = form_obj
|
final_forms[form_key] = form_obj
|
||||||
|
|
||||||
# render the done view and reset the wizard before returning the
|
# render the done view and reset the wizard before returning the
|
||||||
# response. This is needed to prevent from rendering done with the
|
# response. This is needed to prevent from rendering done with the
|
||||||
# same data twice.
|
# same data twice.
|
||||||
done_response = self.done(final_forms.values(), form_dict=final_forms, **kwargs)
|
done_response = self.done(final_forms.values(),
|
||||||
|
form_dict=final_forms,
|
||||||
|
**kwargs)
|
||||||
self.storage.reset()
|
self.storage.reset()
|
||||||
return done_response
|
return done_response
|
||||||
|
|
||||||
@@ -408,7 +414,8 @@ class WizardView(TemplateView):
|
|||||||
'prefix': self.get_form_prefix(step, form_class),
|
'prefix': self.get_form_prefix(step, form_class),
|
||||||
'initial': self.get_form_initial(step),
|
'initial': self.get_form_initial(step),
|
||||||
})
|
})
|
||||||
if issubclass(form_class, (forms.ModelForm, forms.models.BaseInlineFormSet)):
|
if issubclass(form_class, (forms.ModelForm,
|
||||||
|
forms.models.BaseInlineFormSet)):
|
||||||
# If the form is based on ModelForm or InlineFormSet,
|
# If the form is based on ModelForm or InlineFormSet,
|
||||||
# add instance if available and not previously set.
|
# add instance if available and not previously set.
|
||||||
kwargs.setdefault('instance', self.get_form_instance(step))
|
kwargs.setdefault('instance', self.get_form_instance(step))
|
||||||
@@ -544,7 +551,8 @@ class WizardView(TemplateView):
|
|||||||
|
|
||||||
class MyWizard(WizardView):
|
class MyWizard(WizardView):
|
||||||
def get_context_data(self, form, **kwargs):
|
def get_context_data(self, form, **kwargs):
|
||||||
context = super(MyWizard, self).get_context_data(form=form, **kwargs)
|
context = super(MyWizard, self).get_context_data(form=form,
|
||||||
|
**kwargs)
|
||||||
if self.steps.current == 'my_step_name':
|
if self.steps.current == 'my_step_name':
|
||||||
context.update({'another_var': True})
|
context.update({'another_var': True})
|
||||||
return context
|
return context
|
||||||
@@ -581,14 +589,14 @@ class SessionWizardView(WizardView):
|
|||||||
"""
|
"""
|
||||||
A WizardView with pre-configured SessionStorage backend.
|
A WizardView with pre-configured SessionStorage backend.
|
||||||
"""
|
"""
|
||||||
storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage'
|
storage_name = 'formtools.wizard.storage.session.SessionStorage'
|
||||||
|
|
||||||
|
|
||||||
class CookieWizardView(WizardView):
|
class CookieWizardView(WizardView):
|
||||||
"""
|
"""
|
||||||
A WizardView with pre-configured CookieStorage backend.
|
A WizardView with pre-configured CookieStorage backend.
|
||||||
"""
|
"""
|
||||||
storage_name = 'django.contrib.formtools.wizard.storage.cookie.CookieStorage'
|
storage_name = 'formtools.wizard.storage.cookie.CookieStorage'
|
||||||
|
|
||||||
|
|
||||||
class NamedUrlWizardView(WizardView):
|
class NamedUrlWizardView(WizardView):
|
||||||
@@ -604,16 +612,19 @@ class NamedUrlWizardView(WizardView):
|
|||||||
We require a url_name to reverse URLs later. Additionally users can
|
We require a url_name to reverse URLs later. Additionally users can
|
||||||
pass a done_step_name to change the URL name of the "done" view.
|
pass a done_step_name to change the URL name of the "done" view.
|
||||||
"""
|
"""
|
||||||
assert 'url_name' in kwargs, 'URL name is needed to resolve correct wizard URLs'
|
assert 'url_name' in kwargs, \
|
||||||
|
'URL name is needed to resolve correct wizard URLs'
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'done_step_name': kwargs.pop('done_step_name', 'done'),
|
'done_step_name': kwargs.pop('done_step_name', 'done'),
|
||||||
'url_name': kwargs.pop('url_name'),
|
'url_name': kwargs.pop('url_name'),
|
||||||
}
|
}
|
||||||
initkwargs = super(NamedUrlWizardView, cls).get_initkwargs(*args, **kwargs)
|
initkwargs = super(NamedUrlWizardView, cls).get_initkwargs(*args,
|
||||||
|
**kwargs)
|
||||||
initkwargs.update(extra_kwargs)
|
initkwargs.update(extra_kwargs)
|
||||||
|
|
||||||
assert initkwargs['done_step_name'] not in initkwargs['form_list'], \
|
assert initkwargs['done_step_name'] not in initkwargs['form_list'], \
|
||||||
'step name "%s" is reserved for "done" view' % initkwargs['done_step_name']
|
'step name "%s" is reserved for "done" view' % \
|
||||||
|
initkwargs['done_step_name']
|
||||||
return initkwargs
|
return initkwargs
|
||||||
|
|
||||||
def get_step_url(self, step):
|
def get_step_url(self, step):
|
||||||
@@ -679,7 +690,8 @@ class NamedUrlWizardView(WizardView):
|
|||||||
NamedUrlWizardView provides the url_name of this wizard in the context
|
NamedUrlWizardView provides the url_name of this wizard in the context
|
||||||
dict `wizard`.
|
dict `wizard`.
|
||||||
"""
|
"""
|
||||||
context = super(NamedUrlWizardView, self).get_context_data(form=form, **kwargs)
|
context = super(NamedUrlWizardView, self).get_context_data(form=form,
|
||||||
|
**kwargs)
|
||||||
context['wizard']['url_name'] = self.url_name
|
context['wizard']['url_name'] = self.url_name
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@@ -722,11 +734,11 @@ class NamedUrlSessionWizardView(NamedUrlWizardView):
|
|||||||
"""
|
"""
|
||||||
A NamedUrlWizardView with pre-configured SessionStorage backend.
|
A NamedUrlWizardView with pre-configured SessionStorage backend.
|
||||||
"""
|
"""
|
||||||
storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage'
|
storage_name = 'formtools.wizard.storage.session.SessionStorage'
|
||||||
|
|
||||||
|
|
||||||
class NamedUrlCookieWizardView(NamedUrlWizardView):
|
class NamedUrlCookieWizardView(NamedUrlWizardView):
|
||||||
"""
|
"""
|
||||||
A NamedUrlFormWizard with pre-configured CookieStorageBackend.
|
A NamedUrlFormWizard with pre-configured CookieStorageBackend.
|
||||||
"""
|
"""
|
||||||
storage_name = 'django.contrib.formtools.wizard.storage.cookie.CookieStorage'
|
storage_name = 'formtools.wizard.storage.cookie.CookieStorage'
|
||||||
|
|||||||
38
tasks.py
Normal file
38
tasks.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import sys
|
||||||
|
from invoke import run, task
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def clean():
|
||||||
|
run('git clean -Xfd')
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def test():
|
||||||
|
print('Python version: ' + sys.version)
|
||||||
|
test_cmd = 'coverage run `which django-admin.py` test --settings=tests.settings'
|
||||||
|
flake_cmd = 'flake8 --ignore=W801,E128,E501,W402'
|
||||||
|
|
||||||
|
# Fix issue #49
|
||||||
|
cwp = os.path.dirname(os.path.abspath(__name__))
|
||||||
|
pythonpath = os.environ.get('PYTHONPATH', '').split(os.pathsep)
|
||||||
|
pythonpath.append(os.path.join(cwp, 'tests'))
|
||||||
|
os.environ['PYTHONPATH'] = os.pathsep.join(pythonpath)
|
||||||
|
|
||||||
|
run('{0} formtools'.format(flake_cmd))
|
||||||
|
run('{0} tests'.format(test_cmd))
|
||||||
|
run('coverage report')
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def translations(pull=False):
|
||||||
|
if pull:
|
||||||
|
run('tx pull -a')
|
||||||
|
run('cd formtools; django-admin.py makemessages -a; django-admin.py compilemessages; cd ..')
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def docs():
|
||||||
|
run('cd docs; make html; cd ..')
|
||||||
6
tests/requirements.txt
Normal file
6
tests/requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# invoke
|
||||||
|
# temporary fix for invoke on Python 3.2
|
||||||
|
invoke==0.9.0
|
||||||
|
coverage==3.7.1
|
||||||
|
flake8==2.2.3
|
||||||
|
six==1.8.0
|
||||||
31
tests/settings.py
Normal file
31
tests/settings.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': ':memory:',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'formtools',
|
||||||
|
'tests.wizard.wizardtests',
|
||||||
|
]
|
||||||
|
|
||||||
|
SECRET_KEY = 'spam-spam-spam-spam'
|
||||||
|
|
||||||
|
CACHES = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||||
|
'LOCATION': 'spam-and-eggs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MIDDLEWARE_CLASSES = (
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||||
|
)
|
||||||
@@ -7,13 +7,12 @@ import unittest
|
|||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from django import http
|
from django import http
|
||||||
from django.contrib.formtools import preview, utils
|
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.utils._os import upath
|
from django.utils._os import upath
|
||||||
|
|
||||||
from django.contrib.formtools.tests.forms import (
|
from formtools import preview, utils
|
||||||
HashTestBlankForm, HashTestForm, TestForm,
|
|
||||||
)
|
from .forms import HashTestBlankForm, HashTestForm, TestForm
|
||||||
|
|
||||||
success_string = "Done was called!"
|
success_string = "Done was called!"
|
||||||
success_string_encoded = success_string.encode()
|
success_string_encoded = success_string.encode()
|
||||||
@@ -36,7 +35,7 @@ class TestFormPreview(preview.FormPreview):
|
|||||||
TEMPLATE_DIRS=(
|
TEMPLATE_DIRS=(
|
||||||
os.path.join(os.path.dirname(upath(__file__)), 'templates'),
|
os.path.join(os.path.dirname(upath(__file__)), 'templates'),
|
||||||
),
|
),
|
||||||
ROOT_URLCONF='django.contrib.formtools.tests.urls',
|
ROOT_URLCONF='tests.urls',
|
||||||
)
|
)
|
||||||
class PreviewTests(TestCase):
|
class PreviewTests(TestCase):
|
||||||
|
|
||||||
@@ -3,9 +3,9 @@ This is a URLconf to be loaded by tests.py. Add any URLs needed for tests only.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.contrib.formtools.tests.tests import TestFormPreview
|
|
||||||
|
|
||||||
from django.contrib.formtools.tests.forms import TestForm
|
from .tests import TestFormPreview
|
||||||
|
from .forms import TestForm
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@@ -9,7 +9,7 @@ from django.template import Template, Context
|
|||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from django.contrib.formtools.wizard.views import NamedUrlWizardView
|
from formtools.wizard.views import NamedUrlWizardView
|
||||||
|
|
||||||
temp_storage_location = tempfile.mkdtemp(dir=os.environ.get('DJANGO_TEST_TEMP_DIR'))
|
temp_storage_location = tempfile.mkdtemp(dir=os.environ.get('DJANGO_TEST_TEMP_DIR'))
|
||||||
temp_storage = FileSystemStorage(location=temp_storage_location)
|
temp_storage = FileSystemStorage(location=temp_storage_location)
|
||||||
@@ -51,8 +51,8 @@ class ContactWizard(NamedUrlWizardView):
|
|||||||
|
|
||||||
|
|
||||||
class SessionContactWizard(ContactWizard):
|
class SessionContactWizard(ContactWizard):
|
||||||
storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage'
|
storage_name = 'formtools.wizard.storage.session.SessionStorage'
|
||||||
|
|
||||||
|
|
||||||
class CookieContactWizard(ContactWizard):
|
class CookieContactWizard(ContactWizard):
|
||||||
storage_name = 'django.contrib.formtools.wizard.storage.cookie.CookieStorage'
|
storage_name = 'formtools.wizard.storage.cookie.CookieStorage'
|
||||||
@@ -10,11 +10,11 @@ from django.utils._os import upath
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||||
|
|
||||||
from django.contrib.formtools.wizard.views import (NamedUrlSessionWizardView,
|
from formtools.wizard.views import (NamedUrlSessionWizardView,
|
||||||
NamedUrlCookieWizardView)
|
NamedUrlCookieWizardView)
|
||||||
from django.contrib.formtools.tests.wizard.test_forms import get_request, Step1, Step2
|
|
||||||
|
|
||||||
from .forms import temp_storage
|
from .forms import temp_storage
|
||||||
|
from ..test_forms import get_request, Step1, Step2
|
||||||
|
|
||||||
|
|
||||||
# On Python 2, __file__ may end with .pyc
|
# On Python 2, __file__ may end with .pyc
|
||||||
@@ -308,7 +308,7 @@ class NamedWizardTests(object):
|
|||||||
|
|
||||||
|
|
||||||
@skipIfCustomUser
|
@skipIfCustomUser
|
||||||
@override_settings(ROOT_URLCONF='django.contrib.formtools.tests.wizard.namedwizardtests.urls')
|
@override_settings(ROOT_URLCONF='tests.wizard.namedwizardtests.urls')
|
||||||
class NamedSessionWizardTests(NamedWizardTests, TestCase):
|
class NamedSessionWizardTests(NamedWizardTests, TestCase):
|
||||||
wizard_urlname = 'nwiz_session'
|
wizard_urlname = 'nwiz_session'
|
||||||
wizard_step_1_data = {
|
wizard_step_1_data = {
|
||||||
@@ -341,7 +341,7 @@ class NamedSessionWizardTests(NamedWizardTests, TestCase):
|
|||||||
|
|
||||||
|
|
||||||
@skipIfCustomUser
|
@skipIfCustomUser
|
||||||
@override_settings(ROOT_URLCONF='django.contrib.formtools.tests.wizard.namedwizardtests.urls')
|
@override_settings(ROOT_URLCONF='tests.wizard.namedwizardtests.urls')
|
||||||
class NamedCookieWizardTests(NamedWizardTests, TestCase):
|
class NamedCookieWizardTests(NamedWizardTests, TestCase):
|
||||||
wizard_urlname = 'nwiz_cookie'
|
wizard_urlname = 'nwiz_cookie'
|
||||||
wizard_step_1_data = {
|
wizard_step_1_data = {
|
||||||
@@ -402,14 +402,14 @@ class TestNamedUrlCookieWizardView(NamedUrlCookieWizardView):
|
|||||||
|
|
||||||
|
|
||||||
@skipIfCustomUser
|
@skipIfCustomUser
|
||||||
@override_settings(ROOT_URLCONF='django.contrib.formtools.tests.wizard.namedwizardtests.urls')
|
@override_settings(ROOT_URLCONF='tests.wizard.namedwizardtests.urls')
|
||||||
class NamedSessionFormTests(NamedFormTests, TestCase):
|
class NamedSessionFormTests(NamedFormTests, TestCase):
|
||||||
formwizard_class = TestNamedUrlSessionWizardView
|
formwizard_class = TestNamedUrlSessionWizardView
|
||||||
wizard_urlname = 'nwiz_session'
|
wizard_urlname = 'nwiz_session'
|
||||||
|
|
||||||
|
|
||||||
@skipIfCustomUser
|
@skipIfCustomUser
|
||||||
@override_settings(ROOT_URLCONF='django.contrib.formtools.tests.wizard.namedwizardtests.urls')
|
@override_settings(ROOT_URLCONF='tests.wizard.namedwizardtests.urls')
|
||||||
class NamedCookieFormTests(NamedFormTests, TestCase):
|
class NamedCookieFormTests(NamedFormTests, TestCase):
|
||||||
formwizard_class = TestNamedUrlCookieWizardView
|
formwizard_class = TestNamedUrlCookieWizardView
|
||||||
wizard_urlname = 'nwiz_cookie'
|
wizard_urlname = 'nwiz_cookie'
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.contrib.formtools.tests.wizard.namedwizardtests.forms import (
|
from .forms import (SessionContactWizard, CookieContactWizard,
|
||||||
SessionContactWizard, CookieContactWizard, Page1, Page2, Page3, Page4)
|
Page1, Page2, Page3, Page4)
|
||||||
|
|
||||||
|
|
||||||
def get_named_session_wizard():
|
def get_named_session_wizard():
|
||||||
@@ -3,8 +3,9 @@ from django.core import signing
|
|||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||||
from django.contrib.formtools.wizard.storage.cookie import CookieStorage
|
from formtools.wizard.storage.cookie import CookieStorage
|
||||||
from django.contrib.formtools.tests.wizard.storage import get_request, TestStorage
|
|
||||||
|
from .storage import get_request, TestStorage
|
||||||
|
|
||||||
|
|
||||||
@skipIfCustomUser
|
@skipIfCustomUser
|
||||||
@@ -10,7 +10,7 @@ from django.template.response import TemplateResponse
|
|||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from django.contrib.formtools.wizard.views import (WizardView,
|
from formtools.wizard.views import (WizardView,
|
||||||
SessionWizardView,
|
SessionWizardView,
|
||||||
CookieWizardView)
|
CookieWizardView)
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ TestModelFormSet = forms.models.modelformset_factory(TestModel, form=TestModelFo
|
|||||||
|
|
||||||
|
|
||||||
class TestWizard(WizardView):
|
class TestWizard(WizardView):
|
||||||
storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage'
|
storage_name = 'formtools.wizard.storage.session.SessionStorage'
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
response = super(TestWizard, self).dispatch(request, *args, **kwargs)
|
response = super(TestWizard, self).dispatch(request, *args, **kwargs)
|
||||||
17
tests/wizard/test_loadstorage.py
Normal file
17
tests/wizard/test_loadstorage.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from formtools.wizard.storage import get_storage, MissingStorage
|
||||||
|
from formtools.wizard.storage.base import BaseStorage
|
||||||
|
|
||||||
|
|
||||||
|
class TestLoadStorage(TestCase):
|
||||||
|
def test_load_storage(self):
|
||||||
|
self.assertEqual(
|
||||||
|
type(get_storage('formtools.wizard.storage.base.BaseStorage', 'wizard1')),
|
||||||
|
BaseStorage)
|
||||||
|
|
||||||
|
def test_missing_storage(self):
|
||||||
|
self.assertRaises(MissingStorage, get_storage,
|
||||||
|
'formtools.wizard.storage.idontexist.IDontExistStorage', 'wizard1')
|
||||||
|
self.assertRaises(MissingStorage, get_storage,
|
||||||
|
'formtools.wizard.storage.base.IDontExistStorage', 'wizard1')
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||||
from django.contrib.formtools.tests.wizard.storage import TestStorage
|
from formtools.wizard.storage.session import SessionStorage
|
||||||
from django.contrib.formtools.wizard.storage.session import SessionStorage
|
|
||||||
|
from .storage import TestStorage
|
||||||
|
|
||||||
|
|
||||||
@skipIfCustomUser
|
@skipIfCustomUser
|
||||||
@@ -9,7 +9,7 @@ from django.forms.models import modelformset_factory
|
|||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.template import Template, Context
|
from django.template import Template, Context
|
||||||
|
|
||||||
from django.contrib.formtools.wizard.views import WizardView
|
from formtools.wizard.views import WizardView
|
||||||
|
|
||||||
|
|
||||||
temp_storage_location = tempfile.mkdtemp(dir=os.environ.get('DJANGO_TEST_TEMP_DIR'))
|
temp_storage_location = tempfile.mkdtemp(dir=os.environ.get('DJANGO_TEST_TEMP_DIR'))
|
||||||
@@ -65,8 +65,8 @@ UserFormSet = modelformset_factory(User, form=UserForm)
|
|||||||
|
|
||||||
|
|
||||||
class SessionContactWizard(ContactWizard):
|
class SessionContactWizard(ContactWizard):
|
||||||
storage_name = 'django.contrib.formtools.wizard.storage.session.SessionStorage'
|
storage_name = 'formtools.wizard.storage.session.SessionStorage'
|
||||||
|
|
||||||
|
|
||||||
class CookieContactWizard(ContactWizard):
|
class CookieContactWizard(ContactWizard):
|
||||||
storage_name = 'django.contrib.formtools.wizard.storage.cookie.CookieStorage'
|
storage_name = 'formtools.wizard.storage.cookie.CookieStorage'
|
||||||
@@ -9,10 +9,11 @@ from django.test.client import RequestFactory
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||||
from django.contrib.formtools.wizard.views import CookieWizardView
|
|
||||||
from django.utils._os import upath
|
from django.utils._os import upath
|
||||||
from django.contrib.formtools.tests.models import Poet, Poem
|
|
||||||
|
|
||||||
|
from formtools.wizard.views import CookieWizardView
|
||||||
|
|
||||||
|
from .models import Poet, Poem
|
||||||
from .forms import temp_storage
|
from .forms import temp_storage
|
||||||
|
|
||||||
|
|
||||||
@@ -233,7 +234,7 @@ class WizardTests(object):
|
|||||||
|
|
||||||
|
|
||||||
@skipIfCustomUser
|
@skipIfCustomUser
|
||||||
@override_settings(ROOT_URLCONF='django.contrib.formtools.tests.wizard.wizardtests.urls')
|
@override_settings(ROOT_URLCONF='tests.wizard.wizardtests.urls')
|
||||||
class SessionWizardTests(WizardTests, TestCase):
|
class SessionWizardTests(WizardTests, TestCase):
|
||||||
wizard_url = '/wiz_session/'
|
wizard_url = '/wiz_session/'
|
||||||
wizard_step_1_data = {
|
wizard_step_1_data = {
|
||||||
@@ -266,7 +267,7 @@ class SessionWizardTests(WizardTests, TestCase):
|
|||||||
|
|
||||||
|
|
||||||
@skipIfCustomUser
|
@skipIfCustomUser
|
||||||
@override_settings(ROOT_URLCONF='django.contrib.formtools.tests.wizard.wizardtests.urls')
|
@override_settings(ROOT_URLCONF='tests.wizard.wizardtests.urls')
|
||||||
class CookieWizardTests(WizardTests, TestCase):
|
class CookieWizardTests(WizardTests, TestCase):
|
||||||
wizard_url = '/wiz_cookie/'
|
wizard_url = '/wiz_cookie/'
|
||||||
wizard_step_1_data = {
|
wizard_step_1_data = {
|
||||||
@@ -299,7 +300,7 @@ class CookieWizardTests(WizardTests, TestCase):
|
|||||||
|
|
||||||
|
|
||||||
@skipIfCustomUser
|
@skipIfCustomUser
|
||||||
@override_settings(ROOT_URLCONF='django.contrib.formtools.tests.wizard.wizardtests.urls')
|
@override_settings(ROOT_URLCONF='tests.wizard.wizardtests.urls')
|
||||||
class WizardTestKwargs(TestCase):
|
class WizardTestKwargs(TestCase):
|
||||||
wizard_url = '/wiz_other_template/'
|
wizard_url = '/wiz_other_template/'
|
||||||
wizard_step_1_data = {
|
wizard_step_1_data = {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.contrib.formtools.tests.wizard.wizardtests.forms import (
|
from .forms import (SessionContactWizard, CookieContactWizard,
|
||||||
SessionContactWizard, CookieContactWizard, Page1, Page2, Page3, Page4)
|
Page1, Page2, Page3, Page4)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^wiz_session/$', SessionContactWizard.as_view(
|
url(r'^wiz_session/$', SessionContactWizard.as_view(
|
||||||
47
tox.ini
Normal file
47
tox.ini
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
[tox]
|
||||||
|
args_are_paths = false
|
||||||
|
envlist =
|
||||||
|
py27-1.7, py27-master,
|
||||||
|
py32-1.7, py32-master,
|
||||||
|
py33-1.7, py33-master
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
usedevelop = true
|
||||||
|
commands =
|
||||||
|
invoke test {posargs}
|
||||||
|
django15 =
|
||||||
|
Django>=1.5,<1.6
|
||||||
|
-r{toxinidir}/tests/requirements.txt
|
||||||
|
django16 =
|
||||||
|
Django>=1.6,<1.7
|
||||||
|
-r{toxinidir}/tests/requirements.txt
|
||||||
|
django17 =
|
||||||
|
Django>=1.7,<1.8
|
||||||
|
-r{toxinidir}/tests/requirements.txt
|
||||||
|
djangomaster =
|
||||||
|
https://github.com/django/django/archive/master.zip
|
||||||
|
-r{toxinidir}/tests/requirements.txt
|
||||||
|
|
||||||
|
[testenv:py27-1.7]
|
||||||
|
basepython = python2.7
|
||||||
|
deps = {[testenv]django17}
|
||||||
|
|
||||||
|
[testenv:py27-master]
|
||||||
|
basepython = python2.7
|
||||||
|
deps = {[testenv]djangomaster}
|
||||||
|
|
||||||
|
[testenv:py32-1.7]
|
||||||
|
basepython = python3.2
|
||||||
|
deps = {[testenv]django17}
|
||||||
|
|
||||||
|
[testenv:py32-master]
|
||||||
|
basepython = python3.2
|
||||||
|
deps = {[testenv]djangomaster}
|
||||||
|
|
||||||
|
[testenv:py33-1.7]
|
||||||
|
basepython = python3.3
|
||||||
|
deps = {[testenv]django17}
|
||||||
|
|
||||||
|
[testenv:py33-master]
|
||||||
|
basepython = python3.3
|
||||||
|
deps = {[testenv]djangomaster}
|
||||||
Reference in New Issue
Block a user