login page

This commit is contained in:
Alicja Cięciwa
2020-10-27 12:57:58 +01:00
commit cb8886666c
8545 changed files with 1082463 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
import pytest
from crispy_forms.layout import HTML, Div, Field, Fieldset, Layout, Submit
only_uni_form = pytest.mark.only("uni_form")
only_bootstrap = pytest.mark.only("bootstrap", "bootstrap3", "bootstrap4")
only_bootstrap3 = pytest.mark.only("bootstrap3")
only_bootstrap4 = pytest.mark.only("bootstrap4")
@pytest.fixture
def advanced_layout():
return Layout(
Div(
Div(Div("email")),
Div(Field("password1")),
Submit("save", "save"),
Fieldset("legend", "first_name", HTML("extra text"),),
Layout("password2",),
),
"last_name",
)
@pytest.fixture(autouse=True, params=("uni_form", "bootstrap", "bootstrap3", "bootstrap4"))
def template_packs(request, settings):
check_template_pack(request.node, request.param)
settings.CRISPY_TEMPLATE_PACK = request.param
def check_template_pack(node, template_pack):
mark = node.get_closest_marker("only")
if mark:
if template_pack not in mark.args:
pytest.skip("Requires %s template pack" % " or ".join(mark.args))

View File

@@ -0,0 +1,163 @@
from django import forms
from django.db import models
from crispy_forms.helper import FormHelper
class SampleForm(forms.Form):
is_company = forms.CharField(label="company", required=False, widget=forms.CheckboxInput())
email = forms.EmailField(
label="email", max_length=30, required=True, widget=forms.TextInput(), help_text="Insert your email"
)
password1 = forms.CharField(label="password", max_length=30, required=True, widget=forms.PasswordInput())
password2 = forms.CharField(label="re-enter password", max_length=30, required=True, widget=forms.PasswordInput())
first_name = forms.CharField(label="first name", max_length=5, required=True, widget=forms.TextInput())
last_name = forms.CharField(label="last name", max_length=5, required=True, widget=forms.TextInput())
datetime_field = forms.SplitDateTimeField(label="date time", widget=forms.SplitDateTimeWidget())
def clean(self):
super().clean()
password1 = self.cleaned_data.get("password1", None)
password2 = self.cleaned_data.get("password2", None)
if not password1 and not password2 or password1 != password2:
raise forms.ValidationError("Passwords dont match")
return self.cleaned_data
class SampleForm2(SampleForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper(self)
class CheckboxesSampleForm(forms.Form):
checkboxes = forms.MultipleChoiceField(
choices=((1, "Option one"), (2, "Option two"), (3, "Option three")),
initial=(1,),
widget=forms.CheckboxSelectMultiple,
)
alphacheckboxes = forms.MultipleChoiceField(
choices=(("option_one", "Option one"), ("option_two", "Option two"), ("option_three", "Option three")),
initial=("option_two", "option_three"),
widget=forms.CheckboxSelectMultiple,
)
numeric_multiple_checkboxes = forms.MultipleChoiceField(
choices=((1, "Option one"), (2, "Option two"), (3, "Option three")),
initial=(1, 2),
widget=forms.CheckboxSelectMultiple,
)
inline_radios = forms.ChoiceField(
choices=(("option_one", "Option one"), ("option_two", "Option two"),),
widget=forms.RadioSelect,
initial="option_two",
)
class CrispyTestModel(models.Model):
email = models.CharField(max_length=20)
password = models.CharField(max_length=20)
class SampleForm3(forms.ModelForm):
class Meta:
model = CrispyTestModel
fields = ["email", "password"]
exclude = ["password"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper(self)
class SampleForm4(forms.ModelForm):
class Meta:
"""
before Django1.6, one cannot use __all__ shortcut for fields
without getting the following error:
django.core.exceptions.FieldError: Unknown field(s) (a, l, _) specified for CrispyTestModel
because obviously it casts the string to a set
"""
model = CrispyTestModel
fields = "__all__" # eliminate RemovedInDjango18Warning
class SampleForm5(forms.Form):
choices = [
(1, 1),
(2, 2),
(1000, 1000),
]
checkbox_select_multiple = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=choices)
radio_select = forms.ChoiceField(widget=forms.RadioSelect, choices=choices)
pk = forms.IntegerField()
class SampleFormWithMedia(forms.Form):
class Media:
css = {"all": ("test.css",)}
js = ("test.js",)
class SampleFormWithMultiValueField(forms.Form):
multi = forms.SplitDateTimeField()
class CrispyEmptyChoiceTestModel(models.Model):
fruit = models.CharField(choices=[("apple", "Apple"), ("pear", "Pear")], null=True, blank=True,)
class SampleForm6(forms.ModelForm):
class Meta:
"""
When allowing null=True in a model field,
the corresponding field will have a choice
for the empty value.
When the form is initialized by an instance
with initial value None, this choice should
be selected.
"""
model = CrispyEmptyChoiceTestModel
fields = ["fruit"]
widgets = {"fruit": forms.RadioSelect()}
class SampleForm7(forms.ModelForm):
is_company = forms.CharField(label="company", required=False, widget=forms.CheckboxInput())
password2 = forms.CharField(label="re-enter password", max_length=30, required=True, widget=forms.PasswordInput())
class Meta:
model = CrispyTestModel
fields = ("email", "password", "password2")
class SampleForm8(forms.ModelForm):
is_company = forms.CharField(label="company", required=False, widget=forms.CheckboxInput())
password2 = forms.CharField(label="re-enter password", max_length=30, required=True, widget=forms.PasswordInput())
class Meta:
model = CrispyTestModel
fields = ("email", "password2", "password")
class FakeFieldFile:
"""
Quacks like a FieldFile (has a .url and string representation), but
doesn't require us to care about storages etc.
"""
url = "something"
def __str__(self):
return self.url
class FileForm(forms.Form):
file_field = forms.FileField(widget=forms.FileInput)
clearable_file = forms.FileField(widget=forms.ClearableFileInput, required=False, initial=FakeFieldFile())

View File

@@ -0,0 +1,3 @@
{% load crispy_forms_tags %}
{% crispy form %}

View File

@@ -0,0 +1,2 @@
<h1>Special custom field</h1>
{% include 'bootstrap/field.html' %}

View File

@@ -0,0 +1,2 @@
<h1>Special custom form</h1>
{% include "bootstrap/whole_uni_form.html" %}

View File

@@ -0,0 +1,4 @@
<h1>Special custom form with context passthrough</h1>
Got prefix: {{ prefix }}.
{% include "bootstrap/whole_uni_form.html" %}
Got suffix: {{ suffix }}.

View File

@@ -0,0 +1,354 @@
import pytest
from django import forms
from crispy_forms.bootstrap import AppendedText
from crispy_forms.exceptions import DynamicError
from crispy_forms.helper import FormHelper, FormHelpersException
from crispy_forms.layout import HTML, Div, Field, Fieldset, Layout, MultiField
from crispy_forms.tests.forms import SampleForm
from .conftest import only_uni_form
def test_wrap_all_fields():
helper = FormHelper()
layout = Layout("email", "password1", "password2",)
helper.layout = layout
helper.all().wrap(Field, css_class="test-class")
for field in layout.fields:
assert isinstance(field, Field)
assert field.attrs["class"] == "test-class"
assert layout[0][0] == "email"
assert layout[1][0] == "password1"
assert layout[2][0] == "password2"
def test_wrap_selected_fields():
helper = FormHelper()
layout = Layout("email", "password1", "password2",)
helper.layout = layout
helper[1:3].wrap(Field, css_class="test-class")
assert not isinstance(layout.fields[0], Field)
assert isinstance(layout.fields[1], Field)
assert isinstance(layout.fields[2], Field)
helper[0].wrap(Fieldset, "legend", css_class="test-class")
assert isinstance(layout[0], Fieldset)
assert layout[0].legend == "legend"
assert layout[0][0] == "email"
def test_wrap_together_with_slices():
helper = FormHelper()
layout = Layout("email", "password1", "password2",)
helper.layout = layout
helper[1:3].wrap_together(Field, css_class="test-class")
assert layout.fields[0] == "email"
assert isinstance(layout.fields[1], Field)
assert layout.fields[1][0] == "password1"
assert layout.fields[1][1] == "password2"
layout = Layout(Div("email"), "password1", "password2",)
helper.layout = layout
helper[0:3].wrap_together(Field, css_class="test-class")
assert isinstance(layout.fields[0], Field)
assert isinstance(layout.fields[0][0], Div)
assert layout.fields[0][0][0] == "email"
assert layout.fields[0][1] == "password1"
assert layout.fields[0][2] == "password2"
layout = Layout("email", "password1", "password2",)
helper.layout = layout
helper[0].wrap_together(Field, css_class="test-class")
assert isinstance(layout.fields[0], Field)
assert layout.fields[1] == "password1"
assert layout.fields[2] == "password2"
layout = Layout("email", "password1", "password2",)
helper.layout = layout
helper[0].wrap_together(Fieldset, "legend", css_class="test-class")
assert isinstance(layout.fields[0], Fieldset)
assert layout.fields[0].legend == "legend"
assert layout.fields[1] == "password1"
assert layout.fields[2] == "password2"
def test_wrap_together_partial_slices():
helper = FormHelper()
layout = Layout("email", "password1", "password2",)
helper.layout = layout
helper[:2].wrap_together(Field, css_class="test-class")
assert isinstance(layout.fields[0], Field)
assert layout.fields[1] == "password2"
assert layout.fields[0][0] == "email"
assert layout.fields[0][1] == "password1"
helper = FormHelper()
layout = Layout("email", "password1", "password2",)
helper.layout = layout
helper[1:].wrap_together(Field, css_class="test-class")
assert layout.fields[0] == "email"
assert isinstance(layout.fields[1], Field)
assert layout.fields[1][0] == "password1"
assert layout.fields[1][1] == "password2"
def test_update_attributes():
helper = FormHelper()
helper.layout = Layout("email", Field("password1"), "password2",)
helper["password1"].update_attributes(readonly=True)
assert "readonly" in helper.layout[1].attrs
def test_update_attributes_and_wrap_once():
helper = FormHelper()
layout = Layout("email", Field("password1"), "password2",)
helper.layout = layout
helper.filter(Field).update_attributes(readonly=True)
assert isinstance(layout[1], Field)
assert layout[1].attrs == {"readonly": True}
layout = Layout("email", Div(Field("password1")), "password2",)
helper.layout = layout
helper.filter(Field, max_level=2).update_attributes(readonly=True)
assert isinstance(layout[1][0], Field)
assert layout[1][0].attrs == {"readonly": True}
layout = Layout("email", Div(Field("password1")), "password2",)
helper.layout = layout
helper.filter(str, greedy=True).wrap_once(Field)
helper.filter(Field, greedy=True).update_attributes(readonly=True)
assert isinstance(layout[0], Field)
assert isinstance(layout[1][0], Field)
assert isinstance(layout[1][0][0], str)
assert isinstance(layout[2], Field)
assert layout[1][0].attrs == {"readonly": True}
assert layout[0].attrs == {"readonly": True}
assert layout[2].attrs == {"readonly": True}
def test_get_layout_objects():
layout_1 = Layout(Div())
assert layout_1.get_layout_objects(Div) == [[[0], "div"]]
layout_2 = Layout(Div(Div(Div("email")), Div("password1"), "password2"))
assert layout_2.get_layout_objects(Div) == [[[0], "div"]]
assert layout_2.get_layout_objects(Div, max_level=1) == [[[0], "div"], [[0, 0], "div"], [[0, 1], "div"]]
assert layout_2.get_layout_objects(Div, max_level=2) == [
[[0], "div"],
[[0, 0], "div"],
[[0, 0, 0], "div"],
[[0, 1], "div"],
]
layout_3 = Layout("email", Div("password1"), "password2",)
assert layout_3.get_layout_objects(str, max_level=2) == [[[0], "email"], [[1, 0], "password1"], [[2], "password2"]]
layout_4 = Layout(Div(Div("field_name"), "field_name2",), Div("password"), "extra_field")
assert layout_4.get_layout_objects(Div) == [[[0], "div"], [[1], "div"]]
assert layout_4.get_layout_objects(Div, max_level=1) == [[[0], "div"], [[0, 0], "div"], [[1], "div"]]
def test_filter_and_wrap():
helper = FormHelper()
layout = Layout("email", Div("password1"), "password2",)
helper.layout = layout
helper.filter(str).wrap(Field, css_class="test-class")
assert isinstance(layout.fields[0], Field)
assert isinstance(layout.fields[1], Div)
assert isinstance(layout.fields[2], Field)
assert layout[2][0] == "password2"
# Wrapping a div in a div
helper.filter(Div).wrap(Div, css_class="test-class")
assert isinstance(layout.fields[1], Div)
assert isinstance(layout.fields[1].fields[0], Div)
assert layout[1][0][0] == "password1"
def test_filter_and_wrap_side_effects():
helper = FormHelper()
layout = Layout(Div("extra_field", Div("password1"),),)
helper.layout = layout
with pytest.raises(DynamicError):
helper.filter(Div, max_level=2).wrap(Div, css_class="test-class")
def test_get_field_names():
layout_1 = Div("field_name")
assert layout_1.get_field_names() == [[[0], "field_name"]]
layout_2 = Div(Div("field_name"))
assert layout_2.get_field_names() == [[[0, 0], "field_name"]]
layout_3 = Div(Div("field_name"), "password")
assert layout_3.get_field_names() == [[[0, 0], "field_name"], [[1], "password"]]
layout_4 = Div(Div(Div("field_name"), "field_name2",), Div("password"), "extra_field")
assert layout_4.get_field_names() == [
[[0, 0, 0], "field_name"],
[[0, 1], "field_name2"],
[[1, 0], "password"],
[[2], "extra_field"],
]
layout_5 = Div(Div("field_name", "field_name2",), "extra_field")
assert layout_5.get_field_names() == [
[[0, 0], "field_name"],
[[0, 1], "field_name2"],
[[1], "extra_field"],
]
def test_layout_get_field_names():
layout_1 = Layout(Div("field_name"), "password")
assert layout_1.get_field_names() == [
[[0, 0], "field_name"],
[[1], "password"],
]
layout_2 = Layout(Div("field_name"), "password", Fieldset("legend", "extra_field"))
assert layout_2.get_field_names() == [
[[0, 0], "field_name"],
[[1], "password"],
[[2, 0], "extra_field"],
]
layout_3 = Layout(Div(Div(Div("email")), Div("password1"), "password2"))
assert layout_3.get_field_names() == [
[[0, 0, 0, 0], "email"],
[[0, 1, 0], "password1"],
[[0, 2], "password2"],
]
def test_filter_by_widget(advanced_layout):
form = SampleForm()
form.helper = FormHelper(form)
form.helper.layout = advanced_layout
assert form.helper.filter_by_widget(forms.PasswordInput).slice == [
[[0, 1, 0, 0], "password1"],
[[0, 4, 0], "password2"],
]
def test_exclude_by_widget(advanced_layout):
form = SampleForm()
form.helper = FormHelper(form)
form.helper.layout = advanced_layout
assert form.helper.exclude_by_widget(forms.PasswordInput).slice == [
[[0, 0, 0, 0], "email"],
[[0, 3, 0], "first_name"],
[[1], "last_name"],
]
def test_exclude_by_widget_and_wrap(advanced_layout):
form = SampleForm()
form.helper = FormHelper(form)
form.helper.layout = advanced_layout
form.helper.exclude_by_widget(forms.PasswordInput).wrap(Field, css_class="hero")
# Check wrapped fields
assert isinstance(form.helper.layout[0][0][0][0], Field)
assert isinstance(form.helper.layout[0][3][0], Field)
assert isinstance(form.helper.layout[1], Field)
# Check others stay the same
assert isinstance(form.helper.layout[0][3][1], HTML)
assert isinstance(form.helper.layout[0][1][0][0], str)
assert isinstance(form.helper.layout[0][4][0], str)
def test_all_without_layout():
form = SampleForm()
form.helper = FormHelper()
with pytest.raises(FormHelpersException):
form.helper.all().wrap(Div)
def test_filter_by_widget_without_form(advanced_layout):
form = SampleForm()
form.helper = FormHelper()
form.helper.layout = advanced_layout
with pytest.raises(FormHelpersException):
form.helper.filter_by_widget(forms.PasswordInput)
def test_formhelper__getitem__():
helper = FormHelper()
layout = Layout(Div("email"), "password1",)
helper.layout = layout
helper["email"].wrap(Field, css_class="hero")
assert isinstance(layout[0][0], Field)
assert layout[0][0][0] == "email"
helper = FormHelper()
helper.layout = Layout("password1")
helper["password1"].wrap(AppendedText, "extra")
assert isinstance(helper.layout[0], AppendedText)
assert helper.layout[0][0] == "password1"
assert helper.layout[0].text == "extra"
def test_formhelper__setitem__():
helper = FormHelper()
layout = Layout("first_field", Div("email"))
helper.layout = layout
helper[0] = "replaced"
assert layout[0] == "replaced"
def test_formhelper__delitem__and__len__():
helper = FormHelper()
layout = Layout("first_field", Div("email"))
helper.layout = layout
del helper[0]
assert len(helper) == 1
def test__delitem__and__len__layout_object():
layout = Layout("first_field", Div("email"))
del layout[0]
assert len(layout) == 1
def test__getitem__layout_object():
layout = Layout(Div(Div(Div("email")), Div("password1"), "password2"))
assert isinstance(layout[0], Div)
assert isinstance(layout[0][0], Div)
assert isinstance(layout[0][0][0], Div)
assert isinstance(layout[0][1], Div)
assert isinstance(layout[0][1][0], str)
assert isinstance(layout[0][2], str)
def test__getattr__append_layout_object():
layout = Layout(Div("email"))
layout.append("password1")
assert isinstance(layout[0], Div)
assert isinstance(layout[0][0], str)
assert isinstance(layout[1], str)
def test__setitem__layout_object():
layout = Layout(Div("email"))
layout[0][0] = "password1"
assert isinstance(layout[0], Div)
assert layout[0][0] == "password1"
@only_uni_form
def test_filter():
helper = FormHelper()
helper.layout = Layout(Div(MultiField("field_name"), "field_name2",), Div("password"), "extra_field")
assert helper.filter(Div, MultiField).slice == [[[0], "div"], [[1], "div"]]
assert helper.filter(Div, MultiField, max_level=1).slice == [[[0], "div"], [[0, 0], "multifield"], [[1], "div"]]
assert helper.filter(MultiField, max_level=1).slice == [[[0, 0], "multifield"]]

View File

@@ -0,0 +1,937 @@
import re
import pytest
import django
from django import forms
from django.forms.models import formset_factory
from django.middleware.csrf import _get_new_csrf_string
from django.template import Context, Template, TemplateSyntaxError
from django.test.html import parse_html
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from crispy_forms.bootstrap import AppendedText, FieldWithButtons, PrependedAppendedText, PrependedText, StrictButton
from crispy_forms.helper import FormHelper, FormHelpersException
from crispy_forms.layout import Button, Field, Hidden, Layout, MultiField, Reset, Submit
from crispy_forms.templatetags.crispy_forms_tags import CrispyFormNode
from crispy_forms.utils import render_crispy_form
from .conftest import only_bootstrap, only_bootstrap3, only_bootstrap4, only_uni_form
from .forms import SampleForm, SampleForm7, SampleForm8, SampleFormWithMedia, SampleFormWithMultiValueField
def test_inputs(settings):
form_helper = FormHelper()
form_helper.add_input(Submit("my-submit", "Submit", css_class="button white"))
form_helper.add_input(Reset("my-reset", "Reset"))
form_helper.add_input(Hidden("my-hidden", "Hidden"))
form_helper.add_input(Button("my-button", "Button"))
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
c = Context({"form": SampleForm(), "form_helper": form_helper})
html = template.render(c)
assert "button white" in html
assert 'id="submit-id-my-submit"' in html
assert 'id="reset-id-my-reset"' in html
assert 'name="my-hidden"' in html
assert 'id="button-id-my-button"' in html
if settings.CRISPY_TEMPLATE_PACK == "uni_form":
assert "submit submitButton" in html
assert "reset resetButton" in html
assert 'class="button"' in html
else:
assert 'class="btn"' in html
assert "btn btn-primary" in html
assert "btn btn-inverse" in html
if settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert len(re.findall(r"<input[^>]+> <", html)) == 9
else:
assert len(re.findall(r"<input[^>]+> <", html)) == 8
def test_invalid_form_method():
form_helper = FormHelper()
with pytest.raises(FormHelpersException):
form_helper.form_method = "superPost"
def test_form_with_helper_without_layout(settings):
form_helper = FormHelper()
form_helper.form_id = "this-form-rocks"
form_helper.form_class = "forms-that-rock"
form_helper.form_method = "GET"
form_helper.form_action = "simpleAction"
form_helper.form_error_title = "ERRORS"
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy testForm form_helper %}
"""
)
# now we render it, with errors
form = SampleForm({"password1": "wargame", "password2": "god"})
form.is_valid()
c = Context({"testForm": form, "form_helper": form_helper})
html = template.render(c)
# Lets make sure everything loads right
assert html.count("<form") == 1
assert "forms-that-rock" in html
assert 'method="get"' in html
assert 'id="this-form-rocks"' in html
assert 'action="%s"' % reverse("simpleAction") in html
if settings.CRISPY_TEMPLATE_PACK == "uni_form":
assert 'class="uniForm' in html
assert "ERRORS" in html
assert "<li>Passwords dont match</li>" in html
# now lets remove the form tag and render it again. All the True items above
# should now be false because the form tag is removed.
form_helper.form_tag = False
html = template.render(c)
assert "<form" not in html
assert "forms-that-rock" not in html
assert 'method="get"' not in html
assert 'id="this-form-rocks"' not in html
def test_form_show_errors_non_field_errors():
form = SampleForm({"password1": "wargame", "password2": "god"})
form.helper = FormHelper()
form.helper.form_show_errors = True
form.is_valid()
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy testForm %}
"""
)
# First we render with errors
c = Context({"testForm": form})
html = template.render(c)
# Ensure those errors were rendered
assert "<li>Passwords dont match</li>" in html
assert str(_("This field is required.")) in html
assert "error" in html
# Now we render without errors
form.helper.form_show_errors = False
c = Context({"testForm": form})
html = template.render(c)
# Ensure errors were not rendered
assert "<li>Passwords dont match</li>" not in html
assert str(_("This field is required.")) not in html
assert "error" not in html
def test_html5_required():
form = SampleForm()
form.helper = FormHelper()
form.helper.html5_required = True
html = render_crispy_form(form)
# 6 out of 7 fields are required and an extra one for the SplitDateTimeWidget makes 7.
if django.VERSION < (1, 10):
assert html.count('required="required"') == 7
else:
assert len(re.findall(r"\brequired\b", html)) == 7
form = SampleForm()
form.helper = FormHelper()
form.helper.html5_required = False
html = render_crispy_form(form)
def test_media_is_included_by_default_with_uniform():
form = SampleFormWithMedia()
form.helper = FormHelper()
form.helper.template_pack = "uni_form"
html = render_crispy_form(form)
assert "test.css" in html
assert "test.js" in html
def test_media_is_included_by_default_with_bootstrap():
form = SampleFormWithMedia()
form.helper = FormHelper()
form.helper.template_pack = "bootstrap"
html = render_crispy_form(form)
assert "test.css" in html
assert "test.js" in html
def test_media_is_included_by_default_with_bootstrap3():
form = SampleFormWithMedia()
form.helper = FormHelper()
form.helper.template_pack = "bootstrap3"
html = render_crispy_form(form)
assert "test.css" in html
assert "test.js" in html
def test_media_is_included_by_default_with_bootstrap4():
form = SampleFormWithMedia()
form.helper = FormHelper()
form.helper.template_pack = "bootstrap4"
html = render_crispy_form(form)
assert "test.css" in html
assert "test.js" in html
def test_media_removed_when_include_media_is_false_with_uniform():
form = SampleFormWithMedia()
form.helper = FormHelper()
form.helper.template_pack = "uni_form"
form.helper.include_media = False
html = render_crispy_form(form)
assert "test.css" not in html
assert "test.js" not in html
def test_media_removed_when_include_media_is_false_with_bootstrap():
form = SampleFormWithMedia()
form.helper = FormHelper()
form.helper.template_pack = "bootstrap"
form.helper.include_media = False
html = render_crispy_form(form)
assert "test.css" not in html
assert "test.js" not in html
def test_media_removed_when_include_media_is_false_with_bootstrap3():
form = SampleFormWithMedia()
form.helper = FormHelper()
form.helper.template_pack = "bootstrap3"
form.helper.include_media = False
html = render_crispy_form(form)
assert "test.css" not in html
assert "test.js" not in html
def test_media_removed_when_include_media_is_false_with_bootstrap4():
form = SampleFormWithMedia()
form.helper = FormHelper()
form.helper.template_pack = "bootstrap4"
form.helper.include_media = False
html = render_crispy_form(form)
assert "test.css" not in html
assert "test.js" not in html
def test_attrs():
form = SampleForm()
form.helper = FormHelper()
form.helper.attrs = {"id": "TestIdForm", "autocomplete": "off"}
html = render_crispy_form(form)
assert 'autocomplete="off"' in html
assert 'id="TestIdForm"' in html
def test_template_context():
helper = FormHelper()
helper.attrs = {
"id": "test-form",
"class": "test-forms",
"action": "submit/test/form",
"autocomplete": "off",
}
node = CrispyFormNode("form", "helper")
context = node.get_response_dict(helper, {}, False)
assert context["form_id"] == "test-form"
assert context["form_attrs"]["id"] == "test-form"
assert "test-forms" in context["form_class"]
assert "test-forms" in context["form_attrs"]["class"]
assert context["form_action"] == "submit/test/form"
assert context["form_attrs"]["action"] == "submit/test/form"
assert context["form_attrs"]["autocomplete"] == "off"
def test_template_context_using_form_attrs():
helper = FormHelper()
helper.form_id = "test-form"
helper.form_class = "test-forms"
helper.form_action = "submit/test/form"
node = CrispyFormNode("form", "helper")
context = node.get_response_dict(helper, {}, False)
assert context["form_id"] == "test-form"
assert context["form_attrs"]["id"] == "test-form"
assert "test-forms" in context["form_class"]
assert "test-forms" in context["form_attrs"]["class"]
assert context["form_action"] == "submit/test/form"
assert context["form_attrs"]["action"] == "submit/test/form"
def test_template_helper_access():
helper = FormHelper()
helper.form_id = "test-form"
assert helper["form_id"] == "test-form"
def test_without_helper(settings):
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form %}
"""
)
c = Context({"form": SampleForm()})
html = template.render(c)
# Lets make sure everything loads right
assert "<form" in html
assert 'method="post"' in html
assert "action" not in html
if settings.CRISPY_TEMPLATE_PACK == "uni_form":
assert "uniForm" in html
def test_template_pack_override_compact(settings):
current_pack = settings.CRISPY_TEMPLATE_PACK
override_pack = current_pack == "uni_form" and "bootstrap" or "uni_form"
# {% crispy form 'template_pack_name' %}
template = Template(
"""
{%% load crispy_forms_tags %%}
{%% crispy form "%s" %%}
"""
% override_pack
)
c = Context({"form": SampleForm()})
html = template.render(c)
if current_pack == "uni_form":
assert "control-group" in html
else:
assert "uniForm" in html
def test_template_pack_override_verbose(settings):
current_pack = settings.CRISPY_TEMPLATE_PACK
override_pack = current_pack == "uni_form" and "bootstrap" or "uni_form"
# {% crispy form helper 'template_pack_name' %}
template = Template(
"""
{%% load crispy_forms_tags %%}
{%% crispy form form_helper "%s" %%}
"""
% override_pack
)
c = Context({"form": SampleForm(), "form_helper": FormHelper()})
html = template.render(c)
if current_pack == "uni_form":
assert "control-group" in html
else:
assert "uniForm" in html
def test_template_pack_override_wrong():
with pytest.raises(TemplateSyntaxError):
Template(
"""
{% load crispy_forms_tags %}
{% crispy form 'foo' %}
"""
)
def test_invalid_helper(settings):
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
c = Context({"form": SampleForm(), "form_helper": "invalid"})
settings.CRISPY_FAIL_SILENTLY = settings.TEMPLATE_DEBUG = False
with pytest.raises(TypeError):
template.render(c)
def test_formset_with_helper_without_layout(settings):
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy testFormSet formset_helper %}
"""
)
form_helper = FormHelper()
form_helper.form_id = "thisFormsetRocks"
form_helper.form_class = "formsets-that-rock"
form_helper.form_method = "POST"
form_helper.form_action = "simpleAction"
SampleFormSet = formset_factory(SampleForm, extra=3)
testFormSet = SampleFormSet()
c = Context({"testFormSet": testFormSet, "formset_helper": form_helper, "csrf_token": _get_new_csrf_string()})
html = template.render(c)
assert html.count("<form") == 1
assert html.count("csrfmiddlewaretoken") == 1
# Check formset management form
assert "form-TOTAL_FORMS" in html
assert "form-INITIAL_FORMS" in html
assert "form-MAX_NUM_FORMS" in html
assert "formsets-that-rock" in html
assert 'method="post"' in html
assert 'id="thisFormsetRocks"' in html
assert 'action="%s"' % reverse("simpleAction") in html
if settings.CRISPY_TEMPLATE_PACK == "uni_form":
assert 'class="uniForm' in html
def test_CSRF_token_POST_form():
form_helper = FormHelper()
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
# The middleware only initializes the CSRF token when processing a real request
# So using RequestContext or csrf(request) here does not work.
# Instead I set the key `csrf_token` to a CSRF token manually, which `csrf_token` tag uses
c = Context({"form": SampleForm(), "form_helper": form_helper, "csrf_token": _get_new_csrf_string()})
html = template.render(c)
assert "csrfmiddlewaretoken" in html
def test_CSRF_token_GET_form():
form_helper = FormHelper()
form_helper.form_method = "GET"
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
c = Context({"form": SampleForm(), "form_helper": form_helper, "csrf_token": _get_new_csrf_string()})
html = template.render(c)
assert "csrfmiddlewaretoken" not in html
def test_disable_csrf():
form = SampleForm()
helper = FormHelper()
helper.disable_csrf = True
html = render_crispy_form(form, helper, {"csrf_token": _get_new_csrf_string()})
assert "csrf" not in html
def test_render_unmentioned_fields():
test_form = SampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout("email")
test_form.helper.render_unmentioned_fields = True
html = render_crispy_form(test_form)
assert html.count("<input") == 8
def test_render_unmentioned_fields_order():
test_form = SampleForm7()
test_form.helper = FormHelper()
test_form.helper.layout = Layout("email")
test_form.helper.render_unmentioned_fields = True
html = render_crispy_form(test_form)
assert html.count("<input") == 4
assert (
# From layout
html.index('id="div_id_email"')
# From form.Meta.fields
< html.index('id="div_id_password"')
< html.index('id="div_id_password2"')
# From fields
< html.index('id="div_id_is_company"')
)
test_form = SampleForm8()
test_form.helper = FormHelper()
test_form.helper.layout = Layout("email")
test_form.helper.render_unmentioned_fields = True
html = render_crispy_form(test_form)
assert html.count("<input") == 4
assert (
# From layout
html.index('id="div_id_email"')
# From form.Meta.fields
< html.index('id="div_id_password2"')
< html.index('id="div_id_password"')
# From fields
< html.index('id="div_id_is_company"')
)
def test_render_hidden_fields():
from .utils import contains_partial
test_form = SampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout("email")
test_form.helper.render_hidden_fields = True
html = render_crispy_form(test_form)
assert html.count("<input") == 1
# Now hide a couple of fields
for field in ("password1", "password2"):
test_form.fields[field].widget = forms.HiddenInput()
html = render_crispy_form(test_form)
assert html.count("<input") == 3
assert html.count("hidden") == 2
assert contains_partial(html, '<input name="password1" type="hidden"/>')
assert contains_partial(html, '<input name="password2" type="hidden"/>')
def test_render_required_fields():
test_form = SampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout("email")
test_form.helper.render_required_fields = True
html = render_crispy_form(test_form)
assert html.count("<input") == 7
def test_helper_custom_template():
form = SampleForm()
form.helper = FormHelper()
form.helper.template = "custom_form_template.html"
html = render_crispy_form(form)
assert "<h1>Special custom form</h1>" in html
def test_helper_custom_field_template():
form = SampleForm()
form.helper = FormHelper()
form.helper.layout = Layout("password1", "password2",)
form.helper.field_template = "custom_field_template.html"
html = render_crispy_form(form)
assert html.count("<h1>Special custom field</h1>") == 2
def test_helper_custom_field_template_no_layout():
form = SampleForm()
form.helper = FormHelper()
form.helper.field_template = "custom_field_template.html"
html = render_crispy_form(form)
for field in form.fields:
assert html.count('id="div_id_%s"' % field) == 1
assert html.count("<h1>Special custom field</h1>") == len(form.fields)
def test_helper_std_field_template_no_layout():
form = SampleForm()
form.helper = FormHelper()
html = render_crispy_form(form)
for field in form.fields:
assert html.count('id="div_id_%s"' % field) == 1
@only_uni_form
def test_form_show_errors():
form = SampleForm(
{
"email": "invalidemail",
"first_name": "first_name_too_long",
"last_name": "last_name_too_long",
"password1": "yes",
"password2": "yes",
}
)
form.helper = FormHelper()
form.helper.layout = Layout(
Field("email"), Field("first_name"), Field("last_name"), Field("password1"), Field("password2"),
)
form.is_valid()
form.helper.form_show_errors = True
html = render_crispy_form(form)
assert html.count("error") == 9
form.helper.form_show_errors = False
html = render_crispy_form(form)
assert html.count("error") == 0
@only_uni_form
def test_multifield_errors():
form = SampleForm({"email": "invalidemail", "password1": "yes", "password2": "yes"})
form.helper = FormHelper()
form.helper.layout = Layout(MultiField("legend", "email"))
form.is_valid()
form.helper.form_show_errors = True
html = render_crispy_form(form)
assert html.count("error") == 3
# Reset layout for avoiding side effects
form.helper.layout = Layout(MultiField("legend", "email"))
form.helper.form_show_errors = False
html = render_crispy_form(form)
assert html.count("error") == 0
@only_bootstrap3
def test_bootstrap_form_show_errors_bs3():
form = SampleForm(
{
"email": "invalidemail",
"first_name": "first_name_too_long",
"last_name": "last_name_too_long",
"password1": "yes",
"password2": "yes",
}
)
form.helper = FormHelper()
form.helper.layout = Layout(
AppendedText("email", "whatever"),
PrependedText("first_name", "blabla"),
PrependedAppendedText("last_name", "foo", "bar"),
AppendedText("password1", "whatever"),
PrependedText("password2", "blabla"),
)
form.is_valid()
form.helper.form_show_errors = True
html = render_crispy_form(form)
assert html.count("error") == 6
form.helper.form_show_errors = False
html = render_crispy_form(form)
assert html.count("error") == 0
@only_bootstrap4
def test_bootstrap_form_show_errors_bs4():
form = SampleForm(
{
"email": "invalidemail",
"first_name": "first_name_too_long",
"last_name": "last_name_too_long",
"password1": "yes",
"password2": "yes",
}
)
form.helper = FormHelper()
form.helper.layout = Layout(
AppendedText("email", "whatever"),
PrependedText("first_name", "blabla"),
PrependedAppendedText("last_name", "foo", "bar"),
AppendedText("password1", "whatever"),
PrependedText("password2", "blabla"),
)
form.is_valid()
form.helper.form_show_errors = True
html = render_crispy_form(form)
assert html.count("error") == 3
form.helper.form_show_errors = False
html = render_crispy_form(form)
assert html.count("error") == 0
@only_bootstrap
def test_error_text_inline(settings):
form = SampleForm({"email": "invalidemail"})
form.helper = FormHelper()
layout = Layout(
AppendedText("first_name", "wat"), PrependedText("email", "@"), PrependedAppendedText("last_name", "@", "wat"),
)
form.helper.layout = layout
form.is_valid()
html = render_crispy_form(form)
help_class = "help-inline"
help_tag_name = "p"
if settings.CRISPY_TEMPLATE_PACK == "bootstrap3":
help_class = "help-block"
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
help_class = "invalid-feedback"
help_tag_name = "div"
matches = re.findall(r'<span id="error_\d_\w*" class="%s"' % help_class, html, re.MULTILINE)
assert len(matches) == 3
form = SampleForm({"email": "invalidemail"})
form.helper = FormHelper()
form.helper.layout = layout
form.helper.error_text_inline = False
html = render_crispy_form(form)
if settings.CRISPY_TEMPLATE_PACK in ["bootstrap", "bootstrap3"]:
help_class = "help-block"
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
help_class = "invalid-feedback"
help_tag_name = "p"
matches = re.findall(r'<{} id="error_\d_\w*" class="{}"'.format(help_tag_name, help_class), html, re.MULTILINE)
assert len(matches) == 3
@only_bootstrap3
def test_error_and_help_inline_bootstrap3():
form = SampleForm({"email": "invalidemail"})
form.helper = FormHelper()
form.helper.error_text_inline = False
form.helper.help_text_inline = True
form.helper.layout = Layout("email")
form.is_valid()
html = render_crispy_form(form)
# Check that help goes before error, otherwise CSS won't work
help_position = html.find('<span id="hint_id_email" class="help-inline">')
error_position = html.find('<p id="error_1_id_email" class="help-block">')
assert help_position < error_position
# Viceversa
form = SampleForm({"email": "invalidemail"})
form.helper = FormHelper()
form.helper.error_text_inline = True
form.helper.help_text_inline = False
form.helper.layout = Layout("email")
form.is_valid()
html = render_crispy_form(form)
# Check that error goes before help, otherwise CSS won't work
error_position = html.find('<span id="error_1_id_email" class="help-inline">')
help_position = html.find('<div id="hint_id_email" class="help-block">')
assert error_position < help_position
@only_bootstrap4
def test_error_and_help_inline():
form = SampleForm({"email": "invalidemail"})
form.helper = FormHelper()
form.helper.error_text_inline = False
form.helper.help_text_inline = True
form.helper.layout = Layout("email")
form.is_valid()
html = render_crispy_form(form)
# Check that help goes before error, otherwise CSS won't work
help_position = html.find('<span id="hint_id_email" class="help-inline">')
error_position = html.find('<p id="error_1_id_email" class="invalid-feedback">')
assert help_position < error_position
# Viceversa
form = SampleForm({"email": "invalidemail"})
form.helper = FormHelper()
form.helper.error_text_inline = True
form.helper.help_text_inline = False
form.helper.layout = Layout("email")
form.is_valid()
html = render_crispy_form(form)
# Check that error goes before help, otherwise CSS won't work
error_position = html.find('<span id="error_1_id_email" class="help-inline">')
help_position = html.find('<small id="hint_id_email" class="form-text text-muted">')
assert error_position < help_position
@only_bootstrap
def test_form_show_labels():
form = SampleForm()
form.helper = FormHelper()
form.helper.layout = Layout(
"password1",
FieldWithButtons("password2", StrictButton("Confirm")),
PrependedText("first_name", "Mr."),
AppendedText("last_name", "@"),
PrependedAppendedText("datetime_field", "on", "secs"),
)
form.helper.form_show_labels = False
html = render_crispy_form(form)
assert html.count("<label") == 0
@only_bootstrap3
def test_label_class_and_field_class():
form = SampleForm()
form.helper = FormHelper()
form.helper.label_class = "col-lg-2"
form.helper.field_class = "col-lg-8"
html = render_crispy_form(form)
dom = parse_html(html)
snippet = parse_html(
'<div class="form-group"> <div class="controls col-lg-offset-2 col-lg-8"> '
'<div id="div_id_is_company" class="checkbox"> <label for="id_is_company" class=""> '
'<input class="checkboxinput" id="id_is_company" name="is_company" type="checkbox" />company'
)
assert dom.count(snippet)
assert html.count("col-lg-8") == 7
form.helper.label_class = "col-sm-3 col-md-4"
form.helper.field_class = "col-sm-8 col-md-6"
html = render_crispy_form(form)
dom = parse_html(html)
snippet = parse_html(
'<div class="form-group"> <div class="controls col-sm-offset-3 col-md-offset-4 col-sm-8 col-md-6"> '
'<div id="div_id_is_company" class="checkbox"> <label for="id_is_company" class=""> '
'<input class="checkboxinput" id="id_is_company" name="is_company" type="checkbox" />company'
)
assert dom.count(snippet)
assert html.count("col-sm-8") == 7
@only_bootstrap3
def test_template_pack():
form = SampleForm()
form.helper = FormHelper()
form.helper.template_pack = "uni_form"
html = render_crispy_form(form)
assert "form-control" not in html
assert "ctrlHolder" in html
@only_bootstrap4
def test_label_class_and_field_class_bs4():
form = SampleForm()
form.helper = FormHelper()
form.helper.label_class = "col-lg-2"
form.helper.field_class = "col-lg-8"
html = render_crispy_form(form)
assert '<div class="form-group">' in html
assert '<div class="col-lg-8">' in html
assert html.count("col-lg-8") == 7
assert "offset" not in html
form.helper.label_class = "col-sm-3 col-md-4"
form.helper.field_class = "col-sm-8 col-md-6"
html = render_crispy_form(form)
assert '<div class="form-group">' in html
assert '<div class="col-sm-8 col-md-6">' in html
assert html.count("col-sm-8") == 7
assert "offset" not in html
@only_bootstrap4
def test_label_class_and_field_class_bs4_offset_when_horizontal():
# Test col-XX-YY pattern
form = SampleForm()
form.helper = FormHelper()
form.helper.label_class = "col-lg-2"
form.helper.field_class = "col-lg-8"
form.helper.form_class = "form-horizontal"
html = render_crispy_form(form)
assert '<div class="form-group row">' in html
assert '<div class="offset-lg-2 col-lg-8">' in html
assert html.count("col-lg-8") == 7
# Test multi col-XX-YY pattern and col-X pattern
form.helper.label_class = "col-sm-3 col-md-4 col-5 col-lg-4"
form.helper.field_class = "col-sm-8 col-md-6 col-7 col-lg-8"
html = render_crispy_form(form)
assert '<div class="form-group row">' in html
assert '<div class="offset-sm-3 offset-md-4 offset-5 offset-lg-4 col-sm-8 col-md-6 col-7 col-lg-8">' in html
assert html.count("col-sm-8") == 7
assert html.count("col-md-6") == 7
assert html.count("col-7") == 7
assert html.count("col-lg-8") == 7
@only_bootstrap4
def test_form_group_with_form_inline_bs4():
form = SampleForm()
form.helper = FormHelper()
html = render_crispy_form(form)
assert '<div class="form-group">' in html
# .row class shouldn't be together with .form-group in inline forms
form = SampleForm()
form.helper = FormHelper()
form.helper.form_class = "form-inline"
form.helper.field_template = "bootstrap4/layout/inline_field.html"
html = render_crispy_form(form)
assert '<div class="form-group row">' not in html
@only_bootstrap4
def test_template_pack_bs4():
form = SampleForm()
form.helper = FormHelper()
form.helper.template_pack = "uni_form"
html = render_crispy_form(form)
assert "form-control" not in html
assert "ctrlHolder" in html
def test_passthrough_context():
"""
Test to ensure that context is passed through implicitly from outside of
the crispy form into the crispy form templates.
"""
form = SampleForm()
form.helper = FormHelper()
form.helper.template = "custom_form_template_with_context.html"
c = {"prefix": "foo", "suffix": "bar"}
html = render_crispy_form(form, helper=form.helper, context=c)
assert "Got prefix: foo" in html
assert "Got suffix: bar" in html
@only_bootstrap3
def test_bootstrap3_does_add_form_control_class_to_non_multivaluefield():
form = SampleForm()
form.helper = FormHelper()
form.helper.template_pack = "bootstrap3"
html = render_crispy_form(form)
assert "form-control" in html
@only_bootstrap3
def test_bootstrap3_does_not_add_form_control_class_to_multivaluefield():
form = SampleFormWithMultiValueField()
form.helper = FormHelper()
form.helper.template_pack = "bootstrap3"
html = render_crispy_form(form)
assert "form-control" not in html

View File

@@ -0,0 +1,630 @@
import pytest
from django import forms
from django.forms.models import formset_factory, modelformset_factory
from django.middleware.csrf import _get_new_csrf_string
from django.shortcuts import render
from django.template import Context, Template
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from crispy_forms.bootstrap import Field, InlineCheckboxes
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, ButtonHolder, Column, Div, Fieldset, Layout, MultiField, Row, Submit
from crispy_forms.utils import render_crispy_form
from .conftest import only_bootstrap, only_bootstrap3, only_bootstrap4, only_uni_form
from .forms import (
CheckboxesSampleForm,
CrispyEmptyChoiceTestModel,
CrispyTestModel,
FileForm,
SampleForm,
SampleForm2,
SampleForm3,
SampleForm4,
SampleForm6,
)
from .utils import contains_partial
def test_invalid_unicode_characters(settings):
# Adds a BooleanField that uses non valid unicode characters "ñ"
form_helper = FormHelper()
form_helper.add_layout(Layout("españa"))
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
c = Context({"form": SampleForm(), "form_helper": form_helper})
settings.CRISPY_FAIL_SILENTLY = False
with pytest.raises(Exception):
template.render(c)
def test_unicode_form_field():
class UnicodeForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["contraseña"] = forms.CharField()
helper = FormHelper()
helper.layout = Layout("contraseña")
html = render_crispy_form(UnicodeForm())
assert 'id="id_contraseña"' in html
def test_meta_extra_fields_with_missing_fields():
class FormWithMeta(SampleForm):
class Meta:
fields = ("email", "first_name", "last_name")
form = FormWithMeta()
# We remove email field on the go
del form.fields["email"]
form_helper = FormHelper()
form_helper.layout = Layout("first_name",)
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
c = Context({"form": form, "form_helper": form_helper})
html = template.render(c)
assert "email" not in html
def test_layout_unresolved_field(settings):
form_helper = FormHelper()
form_helper.add_layout(Layout("typo"))
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
c = Context({"form": SampleForm(), "form_helper": form_helper})
settings.CRISPY_FAIL_SILENTLY = False
with pytest.raises(Exception):
template.render(c)
def test_double_rendered_field(settings):
form_helper = FormHelper()
form_helper.add_layout(Layout("is_company", "is_company",))
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
c = Context({"form": SampleForm(), "form_helper": form_helper})
settings.CRISPY_FAIL_SILENTLY = False
with pytest.raises(Exception):
template.render(c)
def test_context_pollution():
class ExampleForm(forms.Form):
comment = forms.CharField()
form = ExampleForm()
form2 = SampleForm()
template = Template(
"""
{% load crispy_forms_tags %}
{{ form.as_ul }}
{% crispy form2 %}
{{ form.as_ul }}
"""
)
c = Context({"form": form, "form2": form2})
html = template.render(c)
assert html.count('name="comment"') == 2
assert html.count('name="is_company"') == 1
def test_layout_fieldset_row_html_with_unicode_fieldnames(settings):
form_helper = FormHelper()
form_helper.add_layout(
Layout(
Fieldset(
"Company Data",
"is_company",
css_id="fieldset_company_data",
css_class="fieldsets",
title="fieldset_title",
test_fieldset="123",
),
Fieldset(
"User Data",
"email",
Row("password1", "password2", css_id="row_passwords", css_class="rows",),
HTML('<a href="#" id="testLink">test link</a>'),
HTML(
"""
{% if flag %}{{ message }}{% endif %}
"""
),
"first_name",
"last_name",
),
)
)
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
c = Context({"form": SampleForm(), "form_helper": form_helper, "flag": True, "message": "Hello!"})
html = template.render(c)
assert 'id="fieldset_company_data"' in html
assert 'class="fieldsets' in html
assert 'title="fieldset_title"' in html
assert 'test-fieldset="123"' in html
assert 'id="row_passwords"' in html
assert html.count("<label") == 6
if settings.CRISPY_TEMPLATE_PACK == "uni_form":
assert 'class="formRow rows"' in html
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert 'class="form-row rows"' in html
else:
assert 'class="row rows"' in html
assert "Hello!" in html
assert "testLink" in html
def test_change_layout_dynamically_delete_field():
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
form = SampleForm()
form_helper = FormHelper()
form_helper.add_layout(
Layout(
Fieldset("Company Data", "is_company", "email", "password1", "password2", css_id="multifield_info",),
Column("first_name", "last_name", css_id="column_name",),
)
)
# We remove email field on the go
# Layout needs to be adapted for the new form fields
del form.fields["email"]
del form_helper.layout.fields[0].fields[1]
c = Context({"form": form, "form_helper": form_helper})
html = template.render(c)
assert "email" not in html
def test_column_has_css_classes(settings):
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
form = SampleForm()
form_helper = FormHelper()
form_helper.add_layout(
Layout(
Fieldset("Company Data", "is_company", "email", "password1", "password2", css_id="multifield_info",),
Column("first_name", "last_name",),
)
)
c = Context({"form": form, "form_helper": form_helper})
html = template.render(c)
if settings.CRISPY_TEMPLATE_PACK == "uni_form":
assert html.count("formColumn") == 1
assert html.count("col") == 0
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert html.count("formColumn") == 0
assert html.count("col-md") == 1
@only_bootstrap4
def test_bs4_column_css_classes(settings):
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
form = SampleForm()
form_helper = FormHelper()
form_helper.add_layout(
Layout(
Column("first_name", "last_name",),
Column("first_name", "last_name", css_class="col-sm"),
Column("first_name", "last_name", css_class="mb-4"),
)
)
c = Context({"form": form, "form_helper": form_helper})
html = template.render(c)
assert html.count("col-md") == 2
assert html.count("col-sm") == 1
def test_formset_layout(settings):
SampleFormSet = formset_factory(SampleForm, extra=3)
formset = SampleFormSet()
helper = FormHelper()
helper.form_id = "thisFormsetRocks"
helper.form_class = "formsets-that-rock"
helper.form_method = "POST"
helper.form_action = "simpleAction"
helper.layout = Layout(
Fieldset("Item {{ forloop.counter }}", "is_company", "email",),
HTML("{% if forloop.first %}Note for first form only{% endif %}"),
Row("password1", "password2"),
Fieldset("", "first_name", "last_name"),
)
html = render_crispy_form(form=formset, helper=helper, context={"csrf_token": _get_new_csrf_string()})
# Check formset fields
assert contains_partial(html, '<input id="id_form-TOTAL_FORMS" name="form-TOTAL_FORMS" type="hidden" value="3"/>')
assert contains_partial(
html, '<input id="id_form-INITIAL_FORMS" name="form-INITIAL_FORMS" type="hidden" value="0"/>'
)
assert contains_partial(
html, '<input id="id_form-MAX_NUM_FORMS" name="form-MAX_NUM_FORMS" type="hidden" value="1000"/>'
)
assert contains_partial(
html, '<input id="id_form-MIN_NUM_FORMS" name="form-MIN_NUM_FORMS" type="hidden" value="0"/>'
)
assert html.count("hidden") == 5
# Check form structure
assert html.count("<form") == 1
assert html.count("csrfmiddlewaretoken") == 1
assert "formsets-that-rock" in html
assert 'method="post"' in html
assert 'id="thisFormsetRocks"' in html
assert 'action="%s"' % reverse("simpleAction") in html
# Check form layout
assert "Item 1" in html
assert "Item 2" in html
assert "Item 3" in html
assert html.count("Note for first form only") == 1
if settings.CRISPY_TEMPLATE_PACK == "uni_form":
assert html.count("formRow") == 3
elif settings.CRISPY_TEMPLATE_PACK in ("bootstrap3", "bootstrap4"):
assert html.count("row") == 3
if settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert html.count("form-group") == 18
def test_modelformset_layout():
CrispyModelFormSet = modelformset_factory(CrispyTestModel, form=SampleForm4, extra=3)
formset = CrispyModelFormSet(queryset=CrispyTestModel.objects.none())
helper = FormHelper()
helper.layout = Layout("email")
html = render_crispy_form(form=formset, helper=helper)
assert html.count("id_form-0-id") == 1
assert html.count("id_form-1-id") == 1
assert html.count("id_form-2-id") == 1
assert contains_partial(html, '<input id="id_form-TOTAL_FORMS" name="form-TOTAL_FORMS" type="hidden" value="3"/>')
assert contains_partial(
html, '<input id="id_form-INITIAL_FORMS" name="form-INITIAL_FORMS" type="hidden" value="0"/>'
)
assert contains_partial(
html, '<input id="id_form-MAX_NUM_FORMS" name="form-MAX_NUM_FORMS" type="hidden" value="1000"/>'
)
assert html.count('name="form-0-email"') == 1
assert html.count('name="form-1-email"') == 1
assert html.count('name="form-2-email"') == 1
assert html.count('name="form-3-email"') == 0
assert html.count("password") == 0
def test_i18n():
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form.helper %}
"""
)
form = SampleForm()
form_helper = FormHelper()
form_helper.layout = Layout(HTML(_("i18n text")), Fieldset(_("i18n legend"), "first_name", "last_name",))
form.helper = form_helper
html = template.render(Context({"form": form}))
assert html.count("i18n legend") == 1
def test_default_layout():
test_form = SampleForm2()
assert test_form.helper.layout.fields == [
"is_company",
"email",
"password1",
"password2",
"first_name",
"last_name",
"datetime_field",
]
def test_default_layout_two():
test_form = SampleForm3()
assert test_form.helper.layout.fields == ["email"]
def test_modelform_layout_without_meta():
test_form = SampleForm4()
test_form.helper = FormHelper()
test_form.helper.layout = Layout("email")
html = render_crispy_form(test_form)
assert "email" in html
assert "password" not in html
def test_specialspaceless_not_screwing_intended_spaces():
# see issue #250
test_form = SampleForm()
test_form.fields["email"].widget = forms.Textarea()
test_form.helper = FormHelper()
test_form.helper.layout = Layout("email", HTML("<span>first span</span> <span>second span</span>"))
html = render_crispy_form(test_form)
assert "<span>first span</span> <span>second span</span>" in html
def test_choice_with_none_is_selected():
# see issue #701
model_instance = CrispyEmptyChoiceTestModel()
model_instance.fruit = None
test_form = SampleForm6(instance=model_instance)
html = render_crispy_form(test_form)
assert "checked" in html
@only_uni_form
def test_layout_composition():
form_helper = FormHelper()
form_helper.add_layout(
Layout(
Layout(MultiField("Some company data", "is_company", "email", css_id="multifield_info",),),
Column(
"first_name",
# 'last_name', Missing a field on purpose
css_id="column_name",
css_class="columns",
),
ButtonHolder(Submit("Save", "Save", css_class="button white"),),
Div("password1", "password2", css_id="custom-div", css_class="customdivs",),
)
)
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
c = Context({"form": SampleForm(), "form_helper": form_helper})
html = template.render(c)
assert "multiField" in html
assert "formColumn" in html
assert 'id="multifield_info"' in html
assert 'id="column_name"' in html
assert 'class="formColumn columns"' in html
assert 'class="buttonHolder">' in html
assert 'input type="submit"' in html
assert 'name="Save"' in html
assert 'id="custom-div"' in html
assert 'class="customdivs"' in html
assert "last_name" not in html
@only_uni_form
def test_second_layout_multifield_column_buttonholder_submit_div():
form_helper = FormHelper()
form_helper.add_layout(
Layout(
MultiField(
"Some company data",
"is_company",
"email",
css_id="multifield_info",
title="multifield_title",
multifield_test="123",
),
Column("first_name", "last_name", css_id="column_name", css_class="columns",),
ButtonHolder(
Submit(
"Save the world", "{{ value_var }}", css_class="button white", data_id="test", data_name="test"
),
Submit("store", "Store results"),
),
Div("password1", "password2", css_id="custom-div", css_class="customdivs", test_markup="123"),
)
)
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form form_helper %}
"""
)
c = Context({"form": SampleForm(), "form_helper": form_helper, "value_var": "Save"})
html = template.render(c)
assert "multiField" in html
assert "formColumn" in html
assert 'id="multifield_info"' in html
assert 'title="multifield_title"' in html
assert 'multifield-test="123"' in html
assert 'id="column_name"' in html
assert 'class="formColumn columns"' in html
assert 'class="buttonHolder">' in html
assert 'input type="submit"' in html
assert "button white" in html
assert 'data-id="test"' in html
assert 'data-name="test"' in html
assert 'name="save-the-world"' in html
assert 'value="Save"' in html
assert 'name="store"' in html
assert 'value="Store results"' in html
assert 'id="custom-div"' in html
assert 'class="customdivs"' in html
assert 'test-markup="123"' in html
@only_bootstrap
def test_keepcontext_context_manager(settings):
# Test case for issue #180
# Apparently it only manifest when using render_to_response this exact way
form = CheckboxesSampleForm()
form.helper = FormHelper()
# We use here InlineCheckboxes as it updates context in an unsafe way
form.helper.layout = Layout("checkboxes", InlineCheckboxes("alphacheckboxes"), "numeric_multiple_checkboxes")
context = {"form": form}
response = render(request=None, template_name="crispy_render_template.html", context=context)
if settings.CRISPY_TEMPLATE_PACK == "bootstrap":
assert response.content.count(b"checkbox inline") == 3
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap3":
assert response.content.count(b"checkbox-inline") == 3
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert response.content.count(b"custom-control-inline") == 3
assert response.content.count(b"custom-checkbox") > 0
@only_bootstrap4
def test_use_custom_control_is_used():
form = CheckboxesSampleForm()
form.helper = FormHelper()
form.helper.layout = Layout("checkboxes", InlineCheckboxes("alphacheckboxes"), "numeric_multiple_checkboxes")
# form.helper.use_custom_control take default value which is True
response = render(request=None, template_name="crispy_render_template.html", context={"form": form})
assert response.content.count(b"custom-control-inline") == 3
assert response.content.count(b"custom-checkbox") == 9
form.helper.use_custom_control = True
response = render(request=None, template_name="crispy_render_template.html", context={"form": form})
assert response.content.count(b"custom-control-inline") == 3
assert response.content.count(b"custom-checkbox") == 9
form.helper.use_custom_control = False
response = render(request=None, template_name="crispy_render_template.html", context={"form": form})
assert response.content.count(b"custom-control-inline") == 0
assert response.content.count(b"form-check-inline") == 3
assert response.content.count(b"form-check") > 0
assert response.content.count(b"custom-checkbox") == 0
@only_bootstrap3
def test_form_inline():
form = SampleForm()
form.helper = FormHelper()
form.helper.form_class = "form-inline"
form.helper.field_template = "bootstrap3/layout/inline_field.html"
form.helper.layout = Layout("email", "password1", "last_name",)
html = render_crispy_form(form)
assert html.count('class="form-inline"') == 1
assert html.count('class="form-group"') == 3
assert html.count('<label for="id_email" class="sr-only') == 1
assert html.count('id="div_id_email" class="form-group"') == 1
assert html.count('placeholder="email"') == 1
assert html.count("</label> <input") == 3
@only_bootstrap4
def test_bootstrap4_form_inline():
form = SampleForm()
form.helper = FormHelper()
form.helper.form_class = "form-inline"
form.helper.field_template = "bootstrap4/layout/inline_field.html"
form.helper.layout = Layout("email", "password1", "last_name",)
html = render_crispy_form(form)
assert html.count('class="form-inline"') == 1
assert html.count('class="input-group"') == 3
assert html.count('<label for="id_email" class="sr-only') == 1
assert html.count('id="div_id_email" class="input-group"') == 1
assert html.count('placeholder="email"') == 1
assert html.count("</label> <input") == 3
def test_update_attributes_class():
form = SampleForm()
form.helper = FormHelper()
form.helper.layout = Layout("email", Field("password1"), "password2",)
form.helper["password1"].update_attributes(css_class="hello")
html = render_crispy_form(form)
assert html.count(' class="hello textinput') == 1
form.helper = FormHelper()
form.helper.layout = Layout("email", Field("password1", css_class="hello"), "password2",)
form.helper["password1"].update_attributes(css_class="hello2")
html = render_crispy_form(form)
assert html.count(' class="hello hello2 textinput') == 1
@only_bootstrap4
def test_file_field():
form = FileForm()
form.helper = FormHelper()
form.helper.layout = Layout("clearable_file")
html = render_crispy_form(form)
assert '<span class="custom-control custom-checkbox">' in html
assert '<input type="file" name="clearable_file" class="custom-file-input" >' in html
form.helper.use_custom_control = False
html = render_crispy_form(form)
assert '<input type="checkbox" name="clearable_file-clear" id="clearable_file-clear_id">' in html
assert '<input type="file" name="clearable_file" class="custom-file-input" >' not in html
form.helper.use_custom_control = True
form.helper.layout = Layout("file_field")
html = render_crispy_form(form)
assert '<div class="form-control custom-file"' in html
assert '<input type="file" name="file_field" class="custom-file-input"' in html
assert '<label class="custom-file-label' in html
assert 'for="id_file_field">---</label>' in html
form.helper.use_custom_control = False
html = render_crispy_form(form)
assert "custom-file" not in html
assert "custom-file-input" not in html
assert "custom-file-label" not in html

View File

@@ -0,0 +1,484 @@
from django import forms
from django.template import Context, Template
from django.test.html import parse_html
from django.utils.translation import activate, deactivate
from django.utils.translation import gettext as _
from crispy_forms.bootstrap import (
Accordion,
AccordionGroup,
Alert,
AppendedText,
FieldWithButtons,
InlineCheckboxes,
InlineRadios,
PrependedAppendedText,
PrependedText,
StrictButton,
Tab,
TabHolder,
)
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Field, Layout, MultiWidgetField
from crispy_forms.tests.utils import contains_partial
from crispy_forms.utils import render_crispy_form
from .conftest import only_bootstrap
from .forms import CheckboxesSampleForm, SampleForm
def test_field_with_custom_template():
test_form = SampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout(Field("email", template="custom_field_template.html"))
html = render_crispy_form(test_form)
assert "<h1>Special custom field</h1>" in html
def test_multiwidget_field():
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy form %}
"""
)
test_form = SampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout(
MultiWidgetField(
"datetime_field",
attrs=({"rel": "test_dateinput"}, {"rel": "test_timeinput", "style": "width: 30px;", "type": "hidden"}),
)
)
c = Context({"form": test_form})
html = template.render(c)
assert html.count('class="dateinput') == 1
assert html.count('rel="test_dateinput"') == 1
assert html.count('rel="test_timeinput"') == 2
assert html.count('style="width: 30px;"') == 2
assert html.count('type="hidden"') == 2
def test_field_type_hidden():
template = Template(
"""
{% load crispy_forms_tags %}
{% crispy test_form %}
"""
)
test_form = SampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout(Field("email", type="hidden", data_test=12), Field("datetime_field"),)
c = Context({"test_form": test_form})
html = template.render(c)
# Check form parameters
assert html.count('data-test="12"') == 1
assert html.count('name="email"') == 1
assert html.count('class="dateinput') == 1
assert html.count('class="timeinput') == 1
def test_field_wrapper_class(settings):
form = SampleForm()
form.helper = FormHelper()
form.helper.layout = Layout(Field("email", wrapper_class="testing"))
html = render_crispy_form(form)
if settings.CRISPY_TEMPLATE_PACK == "bootstrap":
assert html.count('class="control-group testing"') == 1
elif settings.CRISPY_TEMPLATE_PACK in ("bootstrap3", "bootstrap4"):
assert html.count('class="form-group testing"') == 1
def test_html_with_carriage_returns(settings):
test_form = SampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout(
HTML(
"""
if (a==b){
// some comment
a+1;
foo();
}
"""
)
)
html = render_crispy_form(test_form)
if settings.CRISPY_TEMPLATE_PACK == "uni_form":
assert html.count("\n") == 23
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap":
assert html.count("\n") == 25
else:
assert html.count("\n") == 27
def test_i18n():
activate("es")
form = SampleForm()
form.helper = FormHelper()
form.helper.layout = Layout(HTML(_("Enter a valid value.")))
html = render_crispy_form(form)
assert "Introduzca un valor válido" in html
deactivate()
def test_remove_labels():
form = SampleForm()
# remove boolean field as label is still printed in boostrap
del form.fields["is_company"]
for fields in form:
fields.label = False
html = render_crispy_form(form)
assert "<label" not in html
@only_bootstrap
class TestBootstrapLayoutObjects:
def test_custom_django_widget(self, settings):
class CustomRadioSelect(forms.RadioSelect):
pass
class CustomCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
pass
# Make sure an inherited RadioSelect gets rendered as it
form = CheckboxesSampleForm()
form.fields["inline_radios"].widget = CustomRadioSelect()
form.helper = FormHelper()
form.helper.layout = Layout("inline_radios")
html = render_crispy_form(form)
if settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert 'class="custom-control-input"' in html
else:
assert 'class="radio"' in html
# Make sure an inherited CheckboxSelectMultiple gets rendered as it
form.fields["checkboxes"].widget = CustomCheckboxSelectMultiple()
form.helper.layout = Layout("checkboxes")
html = render_crispy_form(form)
if settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert 'class="custom-control-input"' in html
else:
assert 'class="checkbox"' in html
def test_prepended_appended_text(self, settings):
test_form = SampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout(
PrependedAppendedText("email", "@", "gmail.com"),
AppendedText("password1", "#"),
PrependedText("password2", "$"),
)
html = render_crispy_form(test_form)
dom = parse_html(html)
# Check form parameters
if settings.CRISPY_TEMPLATE_PACK == "bootstrap":
assert dom.count(parse_html('<span class="add-on">@</span>')) == 1
assert dom.count(parse_html('<span class="add-on">gmail.com</span>')) == 1
assert dom.count(parse_html('<span class="add-on">#</span>')) == 1
assert dom.count(parse_html('<span class="add-on">$</span>')) == 1
if settings.CRISPY_TEMPLATE_PACK == "bootstrap3":
assert html.count('<span class="input-group-addon">@</span>') == 1
assert html.count('<span class="input-group-addon">gmail.com</span>') == 1
assert html.count('<span class="input-group-addon">#</span>') == 1
assert html.count('<span class="input-group-addon">$</span>') == 1
test_form.helper.layout = Layout(PrependedAppendedText("email", "@", "gmail.com", css_class="input-lg"),)
html = render_crispy_form(test_form)
assert 'class="input-lg' in html
assert contains_partial(html, '<span class="input-group-addon input-lg"/>')
if settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert html.count('<span class="input-group-text">@</span>') == 1
assert html.count('<span class="input-group-text">gmail.com</span>') == 1
assert html.count('<span class="input-group-text">#</span>') == 1
assert html.count('<span class="input-group-text">$</span>') == 1
test_form.helper.layout = Layout(
PrependedAppendedText("email", "@", "gmail.com", css_class="form-control-lg"),
)
html = render_crispy_form(test_form)
assert 'class="form-control-lg' in html
assert contains_partial(html, '<span class="input-group-text"/>')
def test_inline_radios(self, settings):
test_form = CheckboxesSampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout(InlineRadios("inline_radios"))
html = render_crispy_form(test_form)
if settings.CRISPY_TEMPLATE_PACK == "bootstrap":
assert html.count('radio inline"') == 2
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap3":
assert html.count('radio-inline"') == 2
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert html.count('custom-control-inline"') == 2
def test_accordion_and_accordiongroup(self, settings):
test_form = SampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout(
Accordion(AccordionGroup("one", "first_name"), AccordionGroup("two", "password1", "password2"))
)
html = render_crispy_form(test_form)
if settings.CRISPY_TEMPLATE_PACK == "bootstrap":
assert html.count('<div class="accordion"') == 1
assert html.count('<div class="accordion-group">') == 2
assert html.count('<div class="accordion-heading">') == 2
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap3":
assert html.count('<div class="panel panel-default"') == 2
assert html.count('<div class="panel-group"') == 1
assert html.count('<div class="panel-heading">') == 2
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert html.count('<div id="accordion"') == 1
assert html.count('<div class="card mb-2"') == 2
assert html.count('<div class="card-header"') == 2
assert html.count('<div id="one"') == 1
assert html.count('<div id="two"') == 1
assert html.count('name="first_name"') == 1
assert html.count('name="password1"') == 1
assert html.count('name="password2"') == 1
def test_accordion_active_false_not_rendered(self, settings):
test_form = SampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout(
Accordion(
AccordionGroup("one", "first_name",),
# there is no ``active`` kwarg here.
)
)
# The first time, there should be one of them there.
html = render_crispy_form(test_form)
if settings.CRISPY_TEMPLATE_PACK == "bootstrap":
accordion_class = "accordion-body collapse in"
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap3":
accordion_class = "panel-collapse collapse in"
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
accordion_class = "collapse show"
assert html.count('<div id="one" class="%s"' % accordion_class) == 1
test_form.helper.layout = Layout(
Accordion(AccordionGroup("one", "first_name", active=False,),) # now ``active`` manually set as False
)
# This time, it shouldn't be there at all.
html = render_crispy_form(test_form)
assert html.count('<div id="one" class="%s collapse in"' % accordion_class) == 0
def test_alert(self):
test_form = SampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout(Alert(content="Testing..."))
html = render_crispy_form(test_form)
assert html.count('<div class="alert"') == 1
assert html.count('<button type="button" class="close"') == 1
assert html.count("Testing...") == 1
def test_alert_block(self):
test_form = SampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout(Alert(content="Testing...", block=True))
html = render_crispy_form(test_form)
assert html.count('<div class="alert alert-block"') == 1
assert html.count("Testing...") == 1
def test_tab_and_tab_holder(self, settings):
test_form = SampleForm()
test_form.helper = FormHelper()
test_form.helper.layout = Layout(
TabHolder(
Tab("one", "first_name", css_id="custom-name", css_class="first-tab-class active"),
Tab("two", "password1", "password2"),
)
)
html = render_crispy_form(test_form)
if settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert (
html.count(
'<ul class="nav nav-tabs"> <li class="nav-item">'
'<a class="nav-link active" href="#custom-name" data-toggle="tab">One</a></li>'
)
== 1
)
assert html.count("tab-pane") == 2
else:
assert (
html.count(
'<ul class="nav nav-tabs"> <li class="tab-pane active">'
'<a href="#custom-name" data-toggle="tab">One</a></li>'
)
== 1
)
assert html.count('<li class="tab-pane') == 2
assert html.count("tab-pane") == 4
assert html.count('class="tab-pane first-tab-class active"') == 1
assert html.count('<div id="custom-name"') == 1
assert html.count('<div id="two"') == 1
assert html.count('name="first_name"') == 1
assert html.count('name="password1"') == 1
assert html.count('name="password2"') == 1
def test_tab_helper_reuse(self):
# this is a proper form, according to the docs.
# note that the helper is a class property here,
# shared between all instances
class SampleForm(forms.Form):
val1 = forms.CharField(required=False)
val2 = forms.CharField(required=True)
helper = FormHelper()
helper.layout = Layout(TabHolder(Tab("one", "val1",), Tab("two", "val2",)))
# first render of form => everything is fine
test_form = SampleForm()
html = render_crispy_form(test_form)
# second render of form => first tab should be active,
# but not duplicate class
test_form = SampleForm()
html = render_crispy_form(test_form)
assert html.count('class="nav-item active active"') == 0
# render a new form, now with errors
test_form = SampleForm(data={"val1": "foo"})
html = render_crispy_form(test_form)
tab_class = "tab-pane"
# if settings.CRISPY_TEMPLATE_PACK == 'bootstrap4':
# tab_class = 'nav-link'
# else:
# tab_class = 'tab-pane'
# tab 1 should not be active
assert html.count('<div id="one" \n class="{} active'.format(tab_class)) == 0
# tab 2 should be active
assert html.count('<div id="two" \n class="{} active'.format(tab_class)) == 1
def test_radio_attrs(self):
form = CheckboxesSampleForm()
form.fields["inline_radios"].widget.attrs = {"class": "first"}
form.fields["checkboxes"].widget.attrs = {"class": "second"}
html = render_crispy_form(form)
assert 'class="first"' in html
assert 'class="second"' in html
def test_field_with_buttons(self, settings):
form = SampleForm()
form.helper = FormHelper()
form.helper.layout = Layout(
FieldWithButtons(
Field("password1", css_class="span4"),
StrictButton("Go!", css_id="go-button"),
StrictButton("No!", css_class="extra"),
StrictButton("Test", type="submit", name="whatever", value="something"),
css_class="extra",
autocomplete="off",
)
)
html = render_crispy_form(form)
form_group_class = "control-group"
if settings.CRISPY_TEMPLATE_PACK in ("bootstrap3", "bootstrap4"):
form_group_class = "form-group"
assert html.count('class="%s extra"' % form_group_class) == 1
assert html.count('autocomplete="off"') == 1
assert html.count('class="span4') == 1
assert html.count('id="go-button"') == 1
assert html.count("Go!") == 1
assert html.count("No!") == 1
assert html.count('class="btn"') == 2
assert html.count('class="btn extra"') == 1
assert html.count('type="submit"') == 1
assert html.count('name="whatever"') == 1
assert html.count('value="something"') == 1
if settings.CRISPY_TEMPLATE_PACK == "bootstrap":
assert html.count('class="input-append"') == 1
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap3":
assert html.count('class="input-group-btn') == 1
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert html.count('class="input-group-append') == 1
def test_hidden_fields(self):
form = SampleForm()
# All fields hidden
for field in form.fields:
form.fields[field].widget = forms.HiddenInput()
form.helper = FormHelper()
form.helper.layout = Layout(
AppendedText("password1", "foo"),
PrependedText("password2", "bar"),
PrependedAppendedText("email", "bar"),
InlineCheckboxes("first_name"),
InlineRadios("last_name"),
)
html = render_crispy_form(form)
assert html.count("<input") == 5
assert html.count('type="hidden"') == 5
assert html.count("<label") == 0
def test_multiplecheckboxes(self, settings):
test_form = CheckboxesSampleForm()
html = render_crispy_form(test_form)
assert html.count('checked="checked"') == 6
test_form.helper = FormHelper(test_form)
test_form.helper[1].wrap(InlineCheckboxes, inline=True)
html = render_crispy_form(test_form)
if settings.CRISPY_TEMPLATE_PACK == "bootstrap":
assert html.count('checkbox inline"') == 3
assert html.count('inline"') == 3
elif settings.CRISPY_TEMPLATE_PACK in ["bootstrap3", "bootstrap4"]:
assert html.count('inline="True"') == 4
if settings.CRISPY_TEMPLATE_PACK == "bootstrap3":
assert html.count('checkbox-inline"') == 3
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert html.count('custom-control-inline"') == 3
def test_multiple_checkboxes_unique_ids(self):
test_form = CheckboxesSampleForm()
html = render_crispy_form(test_form)
expected_ids = [
"checkboxes_1",
"checkboxes_2",
"checkboxes_3",
"alphacheckboxes_1",
"alphacheckboxes_2",
"alphacheckboxes_3",
"numeric_multiple_checkboxes_1",
"numeric_multiple_checkboxes_2",
"numeric_multiple_checkboxes_3",
]
for id_suffix in expected_ids:
expected_str = 'id="id_{id_suffix}"'.format(id_suffix=id_suffix)
assert html.count(expected_str) == 1

View File

@@ -0,0 +1,49 @@
import os
BASE_DIR = os.path.dirname(__file__)
INSTALLED_APPS = (
"django.contrib.auth",
"django.contrib.sessions",
"django.contrib.contenttypes",
"django.contrib.admin",
"crispy_forms",
)
DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}}
MIDDLEWARE_CLASSES = (
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
)
ROOT_URLCONF = "crispy_forms.tests.urls"
CRISPY_CLASS_CONVERTERS = {"textinput": "textinput textInput inputtext"}
SECRET_KEY = "secretkey"
SITE_ROOT = os.path.dirname(os.path.abspath(__file__))
TEMPLATE_DEBUG = True
TEMPLATE_DIRS = (os.path.join(BASE_DIR, "templates"),)
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": TEMPLATE_DIRS,
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
# Insert your TEMPLATE_CONTEXT_PROCESSORS here or use this
# list if you haven't customized them:
"django.contrib.auth.context_processors.auth",
"django.template.context_processors.debug",
"django.template.context_processors.i18n",
"django.template.context_processors.media",
"django.template.context_processors.static",
"django.template.context_processors.tz",
"django.contrib.messages.context_processors.messages",
],
"debug": TEMPLATE_DEBUG,
},
},
]

View File

@@ -0,0 +1,235 @@
import pytest
from django.forms.boundfield import BoundField
from django.forms.formsets import formset_factory
from django.template import Context, Template
from crispy_forms.exceptions import CrispyError
from crispy_forms.templatetags.crispy_forms_field import crispy_addon
from .conftest import only_bootstrap
from .forms import SampleForm
def test_crispy_field():
template = Template(
"""
{% load crispy_forms_field %}
{% for field in form %}
{% crispy_field field %}
{% endfor %}
"""
)
html = template.render(Context({"form": SampleForm()}))
assert html.count("<input") == 8
def test_as_crispy_errors_form_without_non_field_errors():
template = Template(
"""
{% load crispy_forms_tags %}
{{ form|as_crispy_errors }}
"""
)
form = SampleForm({"password1": "god", "password2": "god"})
form.is_valid()
c = Context({"form": form})
html = template.render(c)
assert not ("errorMsg" in html or "alert" in html)
def test_as_crispy_errors_form_with_non_field_errors():
template = Template(
"""
{% load crispy_forms_tags %}
{{ form|as_crispy_errors }}
"""
)
form = SampleForm({"password1": "god", "password2": "wargame"})
form.is_valid()
c = Context({"form": form})
html = template.render(c)
assert "errorMsg" in html or "alert" in html
assert "<li>Passwords dont match</li>" in html
assert "<h3>" not in html
def test_as_crispy_errors_formset_without_non_form_errors():
template = Template(
"""
{% load crispy_forms_tags %}
{{ formset|as_crispy_errors }}
"""
)
SampleFormset = formset_factory(SampleForm, max_num=1, validate_max=True)
formset = SampleFormset()
formset.is_valid()
c = Context({"formset": formset})
html = template.render(c)
assert not ("errorMsg" in html or "alert" in html)
def test_as_crispy_errors_formset_with_non_form_errors():
template = Template(
"""
{% load crispy_forms_tags %}
{{ formset|as_crispy_errors }}
"""
)
SampleFormset = formset_factory(SampleForm, max_num=1, validate_max=True)
formset = SampleFormset(
{
"form-TOTAL_FORMS": "2",
"form-INITIAL_FORMS": "0",
"form-MAX_NUM_FORMS": "",
"form-0-password1": "god",
"form-0-password2": "wargame",
}
)
formset.is_valid()
c = Context({"formset": formset})
html = template.render(c)
assert "errorMsg" in html or "alert" in html
assert "<li>Please submit 1 or fewer forms.</li>" in html
assert "<h3>" not in html
def test_as_crispy_field_non_field(settings):
template = Template(
"""
{% load crispy_forms_tags %}
{{ field|as_crispy_field }}
"""
)
c = Context({"field": "notafield"})
# Raises an AttributeError when trying to figure out how to render it
# Not sure if this is expected behavior -- @kavdev
error_class = CrispyError if settings.DEBUG else AttributeError
with pytest.raises(error_class):
template.render(c)
def test_as_crispy_field_bound_field():
template = Template(
"""
{% load crispy_forms_tags %}
{{ field|as_crispy_field }}
"""
)
form = SampleForm({"password1": "god", "password2": "god"})
form.is_valid()
c = Context({"field": form["password1"]})
# Would raise exception if not a field
html = template.render(c)
assert "id_password1" in html
assert "id_password2" not in html
def test_crispy_filter_with_form():
template = Template(
"""
{% load crispy_forms_tags %}
{{ form|crispy }}
"""
)
c = Context({"form": SampleForm()})
html = template.render(c)
assert "<td>" not in html
assert "id_is_company" in html
assert html.count("<label") == 7
def test_crispy_filter_with_formset():
template = Template(
"""
{% load crispy_forms_tags %}
{{ testFormset|crispy }}
"""
)
SampleFormset = formset_factory(SampleForm, extra=4)
testFormset = SampleFormset()
c = Context({"testFormset": testFormset})
html = template.render(c)
assert html.count("<form") == 0
# Check formset management form
assert "form-TOTAL_FORMS" in html
assert "form-INITIAL_FORMS" in html
assert "form-MAX_NUM_FORMS" in html
def test_classes_filter():
template = Template(
"""
{% load crispy_forms_field %}
{{ testField|classes }}
"""
)
test_form = SampleForm()
test_form.fields["email"].widget.attrs.update({"class": "email-fields"})
c = Context({"testField": test_form.fields["email"]})
html = template.render(c)
assert "email-fields" in html
def test_crispy_field_and_class_converters():
template = Template(
"""
{% load crispy_forms_field %}
{% crispy_field testField 'class' 'error' %}
"""
)
test_form = SampleForm()
field_instance = test_form.fields["email"]
bound_field = BoundField(test_form, field_instance, "email")
c = Context({"testField": bound_field})
html = template.render(c)
assert "error" in html
assert "inputtext" in html
@only_bootstrap
def test_crispy_addon(settings):
test_form = SampleForm()
field_instance = test_form.fields["email"]
bound_field = BoundField(test_form, field_instance, "email")
if settings.CRISPY_TEMPLATE_PACK == "bootstrap":
# prepend tests
assert "input-prepend" in crispy_addon(bound_field, prepend="Work")
assert "input-append" not in crispy_addon(bound_field, prepend="Work")
# append tests
assert "input-prepend" not in crispy_addon(bound_field, append="Primary")
assert "input-append" in crispy_addon(bound_field, append="Secondary")
# prepend and append tests
assert "input-append" in crispy_addon(bound_field, prepend="Work", append="Primary")
assert "input-prepend" in crispy_addon(bound_field, prepend="Work", append="Secondary")
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap3":
assert "input-group-addon" in crispy_addon(bound_field, prepend="Work", append="Primary")
assert "input-group-addon" in crispy_addon(bound_field, prepend="Work", append="Secondary")
elif settings.CRISPY_TEMPLATE_PACK == "bootstrap4":
assert "input-group-text" in crispy_addon(bound_field, prepend="Work", append="Primary")
assert "input-group-text" in crispy_addon(bound_field, prepend="Work", append="Secondary")
# errors
with pytest.raises(TypeError):
crispy_addon()
with pytest.raises(TypeError):
crispy_addon(bound_field)

View File

@@ -0,0 +1,81 @@
from django import forms
from django.template.base import Template
from django.template.context import Context
from django.test import SimpleTestCase
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout
from crispy_forms.tests.utils import contains_partial
from crispy_forms.utils import list_difference, list_intersection, render_field
def test_list_intersection():
assert list_intersection([1, 3], [2, 3]) == [3]
def test_list_difference():
assert list_difference([3, 1, 2, 3], [4, 1]) == [3, 2]
def test_render_field_with_none_field():
rendered = render_field(field=None, form=None, form_style=None, context=None)
assert rendered == ""
def test_custom_bound_field():
from django.forms.boundfield import BoundField
extra = "xyxyxyxyxyx"
class CustomBoundField(BoundField):
@property
def auto_id(self):
return extra
class MyCharField(forms.CharField):
def get_bound_field(self, form, field_name):
return CustomBoundField(form, self, field_name)
class MyForm(forms.Form):
f = MyCharField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout("f")
template = Template('{% load crispy_forms_tags %}\n{% crispy form "bootstrap3" %}')
rendered = template.render(Context({"form": MyForm(data={"f": "something"})}))
assert extra in rendered
def test_contains_partial():
c = SimpleTestCase()
needle = "<span></span>"
html = "<form>%s</form>"
c.assertTrue(contains_partial(html % needle, needle))
needle = "<span></span><b></b>"
c.assertRaises(NotImplementedError, contains_partial, html % needle, needle)
needle = "<span>a</span>"
c.assertRaises(NotImplementedError, contains_partial, html % needle, needle)
needle = '<span id="e"></span>'
html = '<form id="tt"><span id="f"></span>%s</form>'
c.assertTrue(contains_partial(html % needle, needle))
missing = "<script></script>"
c.assertFalse(contains_partial(html % missing, needle))
needle = '<span id="e"></span>'
html = '<form id="tt"><span id="f"></span>%s</form>'
missing = '<span id="g"></span>'
c.assertFalse(contains_partial(html % missing, needle))
needle = '<div id="r"><span>toto</span></div>'
html = '<form><div id="r"></div></form>'
c.assertRaises(NotImplementedError, contains_partial, html, needle)
# as we do not look at the children, needle is equivalent to <div id="r"></div> which IS in html
c.assertTrue(contains_partial(html, needle, ignore_needle_children=True))

View File

@@ -0,0 +1,6 @@
from django.urls import path
from django.views.generic import View
urlpatterns = [
path("simple/action/", View.as_view(), name="simpleAction"),
]

View File

@@ -0,0 +1,22 @@
from django.test.html import Element, parse_html
def contains_partial(haystack, needle, ignore_needle_children=False):
"""Search for a html element with at least the corresponding elements
(other elements may be present in the matched element from the haystack)
"""
if not isinstance(haystack, Element):
haystack = parse_html(haystack)
if not isinstance(needle, Element):
needle = parse_html(needle)
if len(needle.children) > 0 and not ignore_needle_children:
raise NotImplementedError("contains_partial does not check needle's children:%s" % str(needle.children))
if needle.name == haystack.name and set(needle.attributes).issubset(haystack.attributes):
return True
return any(
contains_partial(child, needle, ignore_needle_children=ignore_needle_children)
for child in haystack.children
if isinstance(child, Element)
)