Skip to content

Commit

Permalink
Cleanup Django versions (#717)
Browse files Browse the repository at this point in the history
  • Loading branch information
dyve committed Apr 24, 2024
1 parent 64ce9f7 commit 4ca87b6
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 38 deletions.
23 changes: 6 additions & 17 deletions src/bootstrap4/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
render_label,
)
from .text import text_value
from .utils import DJANGO_VERSION, add_css_class, render_template_file
from .utils import add_css_class, render_template_file

try:
# If Django is set up without a database, importing this widget gives RuntimeError
Expand Down Expand Up @@ -322,22 +322,11 @@ def add_widget_attrs(self):

def list_to_class(self, html, klass):
classes = add_css_class(klass, self.get_size_class())
if DJANGO_VERSION >= 4:
soup = BeautifulSoup(html, features="html.parser")
enclosing_div = soup.find("div")
enclosing_div.attrs["class"] = classes
for inner_div in enclosing_div.find_all("div"):
inner_div.attrs["class"] = inner_div.attrs.get("class", []) + [self.form_check_class]
else:
mapping = [
("<ul", f'<div class="{classes}"'),
("</ul>", "</div>"),
("<li", f'<div class="{self.form_check_class}"'),
("</li>", "</div>"),
]
for k, v in mapping:
html = html.replace(k, v)
soup = BeautifulSoup(html, features="html.parser")
soup = BeautifulSoup(html, features="html.parser")
enclosing_div = soup.find("div")
enclosing_div.attrs["class"] = classes
for inner_div in enclosing_div.find_all("div"):
inner_div.attrs["class"] = inner_div.attrs.get("class", []) + [self.form_check_class]
# Apply bootstrap4 classes to labels and inputs.
# A simple 'replace' isn't enough as we don't want to have several 'class' attr definition, which would happen
# if we tried to 'html.replace("input", "input class=...")'
Expand Down
3 changes: 0 additions & 3 deletions src/bootstrap4/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from collections.abc import Mapping
from urllib.parse import parse_qs, urlparse, urlunparse

from django import get_version
from django.forms.utils import flatatt
from django.template.base import FilterExpression, TemplateSyntaxError, Variable, VariableDoesNotExist, kwarg_re
from django.template.loader import get_template
Expand All @@ -13,8 +12,6 @@

from .text import text_value

DJANGO_VERSION = int(get_version()[:1])

# RegEx for quoted string
QUOTED_STRING = re.compile(r'^["\'](?P<noquotes>.+)["\']$')

Expand Down
61 changes: 45 additions & 16 deletions tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from django.utils.html import escape

from bootstrap4.exceptions import BootstrapError
from bootstrap4.utils import DJANGO_VERSION

from .forms import CharFieldTestForm, TestForm
from .utils import render_field, render_form_field, render_template_with_form
Expand Down Expand Up @@ -37,7 +36,10 @@ def test_help_with_quotes(self):
res = render_form_field("sender")
self.assertIn('title="{}"'.format(escape(TestForm.base_fields["sender"].help_text)), res)
res = render_form_field("cc_myself")
self.assertIn('title="{}"'.format(escape(TestForm.base_fields["cc_myself"].help_text)), res)
self.assertIn(
'title="{}"'.format(escape(TestForm.base_fields["cc_myself"].help_text)),
res,
)

def test_subject(self):
res = render_form_field("subject")
Expand All @@ -54,7 +56,10 @@ def test_xss_field(self):
),
res,
)
self.assertIn(('placeholder="XSS&quot; onmouseover=&quot;alert(&#x27;Hello, XSS&#x27;)&quot; foo=&quot;"'), res)
self.assertIn(
('placeholder="XSS&quot; onmouseover=&quot;alert(&#x27;Hello, XSS&#x27;)&quot; foo=&quot;"'),
res,
)

def test_password(self):
res = render_form_field("password")
Expand Down Expand Up @@ -83,10 +88,9 @@ def test_radio_select(self):
"</div>"
"</div>"
)
if DJANGO_VERSION >= 4:
expected_html = expected_html.replace(
'<label for="id_category1_0">Category1</label>', "<label>Category1</label>"
)
expected_html = expected_html.replace(
'<label for="id_category1_0">Category1</label>', "<label>Category1</label>"
)
self.assertHTMLEqual(res, expected_html)

def test_checkbox(self):
Expand All @@ -97,21 +101,37 @@ def test_checkbox(self):
res = BeautifulSoup(res, "html.parser")
form_group = self._select_one_element(res, ".form-group", "Checkbox should be rendered inside a .form-group.")
form_check = self._select_one_element(
form_group, ".form-check", "There should be a .form-check inside .form-group"
form_group,
".form-check",
"There should be a .form-check inside .form-group",
)
checkbox = self._select_one_element(form_check, "input", "The checkbox should be inside the .form-check")
self.assertIn("form-check-input", checkbox["class"], "The checkbox should have the class 'form-check-input'.")
self.assertIn(
"form-check-input",
checkbox["class"],
"The checkbox should have the class 'form-check-input'.",
)
label = checkbox.next_sibling
self.assertIsNotNone(label, "The label should be rendered after the checkbox.")
self.assertEqual(label.name, "label", "After the checkbox there should be a label.")
self.assertEqual(
label["for"], checkbox["id"], "The for attribute of the label should be the id of the checkbox."
label["for"],
checkbox["id"],
"The for attribute of the label should be the id of the checkbox.",
)
help_text = label.next_sibling
self.assertIsNotNone(help_text, "The help text should be rendered after the label.")
self.assertEqual(help_text.name, "small", "The help text should be rendered as <small> tag.")
self.assertIn("form-text", help_text["class"], "The help text should have the class 'form-text'.")
self.assertIn("text-muted", help_text["class"], "The help text should have the class 'text-muted'.")
self.assertIn(
"form-text",
help_text["class"],
"The help text should have the class 'form-text'.",
)
self.assertIn(
"text-muted",
help_text["class"],
"The help text should have the class 'text-muted'.",
)

def test_checkbox_multiple_select(self):
res = render_form_field("category2")
Expand Down Expand Up @@ -183,7 +203,10 @@ def test_input_group_addon_empty(self):
res = render_template_with_form('{% bootstrap_field form.subject addon_before=None addon_after="after" %}') # noqa
self.assertIn('class="input-group"', res)
self.assertNotIn("input-group-prepend", res)
self.assertIn('<div class="input-group-append"><span class="input-group-text">after</span></div>', res)
self.assertIn(
'<div class="input-group-append"><span class="input-group-text">after</span></div>',
res,
)

def test_input_group_addon_validation(self):
"""
Expand All @@ -194,7 +217,8 @@ def test_input_group_addon_validation(self):
# invalid form data:
data = {"subject": ""}
res = render_template_with_form(
'{% bootstrap_field form.subject addon_before=None addon_after="after" %}', data=data
'{% bootstrap_field form.subject addon_before=None addon_after="after" %}',
data=data,
) # noqa
res = BeautifulSoup(res, "html.parser")
self._select_one_element(
Expand All @@ -204,7 +228,9 @@ def test_input_group_addon_validation(self):
"required, must be placed inside the input-group",
)
self._select_one_element(
res, ".form-group > .form-text", "The form-text message must be placed inside the form-group"
res,
".form-group > .form-text",
"The form-text message must be placed inside the form-group",
)
self.assertEqual(
len(res.select(".form-group > .invalid-feedback")),
Expand Down Expand Up @@ -288,5 +314,8 @@ def test_show_label_skip(self):
def test_for_formset(self):
TestFormSet = formset_factory(CharFieldTestForm, extra=1)
test_formset = TestFormSet()
res = render_template_with_form("{% bootstrap_formset formset show_label=False %}", {"formset": test_formset})
res = render_template_with_form(
"{% bootstrap_formset formset show_label=False %}",
{"formset": test_formset},
)
self.assertIn("sr-only", res)
2 changes: 0 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ setenv =
commands =
python manage.py test {posargs}
deps =
4.0: Django==4.0.*
4.1: Django==4.1.*
4.2: Django==4.2.*
5.0: Django==5.0.*
main: https://github.com/django/django/archive/main.tar.gz
Expand Down

0 comments on commit 4ca87b6

Please sign in to comment.