import copy import datetime from django.forms import ChoiceField, Form, Select from django.test import override_settings from django.utils.safestring import mark_safe from .base import WidgetTest class SelectTest(WidgetTest): widget = Select nested_widget = Select( choices=( ("outer1", "Outer 1"), ('Group "1"', (("inner1", "Inner 1"), ("inner2", "Inner 2"))), ) ) def test_render(self): self.check_html( self.widget(choices=self.beatles), "beatle", "J", html=( """""" ), ) def test_render_none(self): """ If the value is None, none of the options are selected. """ self.check_html( self.widget(choices=self.beatles), "beatle", None, html=( """""" ), ) def test_render_label_value(self): """ If the value corresponds to a label (but not to an option value), none of the options are selected. """ self.check_html( self.widget(choices=self.beatles), "beatle", "John", html=( """""" ), ) def test_render_selected(self): """ Only one option can be selected (#8103). """ choices = [("0", "0"), ("1", "1"), ("2", "2"), ("3", "3"), ("0", "extra")] self.check_html( self.widget(choices=choices), "choices", "0", html=( """""" ), ) def test_constructor_attrs(self): """ Select options shouldn't inherit the parent widget attrs. """ widget = Select( attrs={"class": "super", "id": "super"}, choices=[(1, 1), (2, 2), (3, 3)], ) self.check_html( widget, "num", 2, html=( """""" ), ) def test_compare_to_str(self): """ The value is compared to its str(). """ self.check_html( self.widget(choices=[("1", "1"), ("2", "2"), ("3", "3")]), "num", 2, html=( """""" ), ) self.check_html( self.widget(choices=[(1, 1), (2, 2), (3, 3)]), "num", "2", html=( """""" ), ) self.check_html( self.widget(choices=[(1, 1), (2, 2), (3, 3)]), "num", 2, html=( """""" ), ) def test_choices_constructor(self): widget = Select(choices=[(1, 1), (2, 2), (3, 3)]) self.check_html( widget, "num", 2, html=( """""" ), ) def test_choices_constructor_generator(self): """ If choices is passed to the constructor and is a generator, it can be iterated over multiple times without getting consumed. """ def get_choices(): for i in range(5): yield (i, i) widget = Select(choices=get_choices()) self.check_html( widget, "num", 2, html=( """""" ), ) self.check_html( widget, "num", 3, html=( """""" ), ) def test_choices_escaping(self): choices = (("bad", "you & me"), ("good", mark_safe("you > me"))) self.check_html( self.widget(choices=choices), "escape", None, html=( """""" ), ) def test_choices_unicode(self): self.check_html( self.widget(choices=[("ŠĐĆŽćžšđ", "ŠĐabcĆŽćžšđ"), ("ćžšđ", "abcćžšđ")]), "email", "ŠĐĆŽćžšđ", html=( """ """ ), ) def test_choices_optgroup(self): """ Choices can be nested one level in order to create HTML optgroups. """ self.check_html( self.nested_widget, "nestchoice", None, html=( """""" ), ) def test_choices_select_outer(self): self.check_html( self.nested_widget, "nestchoice", "outer1", html=( """""" ), ) def test_choices_select_inner(self): self.check_html( self.nested_widget, "nestchoice", "inner1", html=( """""" ), ) @override_settings(USE_THOUSAND_SEPARATOR=True) def test_doesnt_localize_option_value(self): choices = [ (1, "One"), (1000, "One thousand"), (1000000, "One million"), ] html = """ """ self.check_html(self.widget(choices=choices), "number", None, html=html) choices = [ (datetime.time(0, 0), "midnight"), (datetime.time(12, 0), "noon"), ] html = """ """ self.check_html(self.widget(choices=choices), "time", None, html=html) def test_options(self): options = list( self.widget(choices=self.beatles).options( "name", ["J"], attrs={"class": "super"}, ) ) self.assertEqual(len(options), 4) self.assertEqual(options[0]["name"], "name") self.assertEqual(options[0]["value"], "J") self.assertEqual(options[0]["label"], "John") self.assertEqual(options[0]["index"], "0") self.assertIs(options[0]["selected"], True) # Template-related attributes self.assertEqual(options[1]["name"], "name") self.assertEqual(options[1]["value"], "P") self.assertEqual(options[1]["label"], "Paul") self.assertEqual(options[1]["index"], "1") self.assertIs(options[1]["selected"], False) def test_optgroups(self): choices = [ ( "Audio", [ ("vinyl", "Vinyl"), ("cd", "CD"), ], ), ( "Video", [ ("vhs", "VHS Tape"), ("dvd", "DVD"), ], ), ("unknown", "Unknown"), ] groups = list( self.widget(choices=choices).optgroups( "name", ["vhs"], attrs={"class": "super"}, ) ) audio, video, unknown = groups label, options, index = audio self.assertEqual(label, "Audio") self.assertEqual( options, [ { "value": "vinyl", "type": "select", "attrs": {}, "index": "0_0", "label": "Vinyl", "template_name": "django/forms/widgets/select_option.html", "name": "name", "selected": False, "wrap_label": True, }, { "value": "cd", "type": "select", "attrs": {}, "index": "0_1", "label": "CD", "template_name": "django/forms/widgets/select_option.html", "name": "name", "selected": False, "wrap_label": True, }, ], ) self.assertEqual(index, 0) label, options, index = video self.assertEqual(label, "Video") self.assertEqual( options, [ { "value": "vhs", "template_name": "django/forms/widgets/select_option.html", "label": "VHS Tape", "attrs": {"selected": True}, "index": "1_0", "name": "name", "selected": True, "type": "select", "wrap_label": True, }, { "value": "dvd", "template_name": "django/forms/widgets/select_option.html", "label": "DVD", "attrs": {}, "index": "1_1", "name": "name", "selected": False, "type": "select", "wrap_label": True, }, ], ) self.assertEqual(index, 1) label, options, index = unknown self.assertIsNone(label) self.assertEqual( options, [ { "value": "unknown", "selected": False, "template_name": "django/forms/widgets/select_option.html", "label": "Unknown", "attrs": {}, "index": "2", "name": "name", "type": "select", "wrap_label": True, } ], ) self.assertEqual(index, 2) def test_optgroups_integer_choices(self): """The option 'value' is the same type as what's in `choices`.""" groups = list( self.widget(choices=[[0, "choice text"]]).optgroups("name", ["vhs"]) ) label, options, index = groups[0] self.assertEqual(options[0]["value"], 0) def test_deepcopy(self): """ __deepcopy__() should copy all attributes properly (#25085). """ widget = Select() obj = copy.deepcopy(widget) self.assertIsNot(widget, obj) self.assertEqual(widget.choices, obj.choices) self.assertIsNot(widget.choices, obj.choices) self.assertEqual(widget.attrs, obj.attrs) self.assertIsNot(widget.attrs, obj.attrs) def test_doesnt_render_required_when_impossible_to_select_empty_field(self): widget = self.widget(choices=[("J", "John"), ("P", "Paul")]) self.assertIs(widget.use_required_attribute(initial=None), False) def test_renders_required_when_possible_to_select_empty_field_str(self): widget = self.widget(choices=[("", "select please"), ("P", "Paul")]) self.assertIs(widget.use_required_attribute(initial=None), True) def test_renders_required_when_possible_to_select_empty_field_list(self): widget = self.widget(choices=[["", "select please"], ["P", "Paul"]]) self.assertIs(widget.use_required_attribute(initial=None), True) def test_renders_required_when_possible_to_select_empty_field_none(self): widget = self.widget(choices=[(None, "select please"), ("P", "Paul")]) self.assertIs(widget.use_required_attribute(initial=None), True) def test_doesnt_render_required_when_no_choices_are_available(self): widget = self.widget(choices=[]) self.assertIs(widget.use_required_attribute(initial=None), False) def test_fieldset(self): class TestForm(Form): template_name = "forms_tests/use_fieldset.html" field = ChoiceField(widget=self.widget, choices=self.beatles) form = TestForm() self.assertIs(self.widget.use_fieldset, False) self.assertHTMLEqual( '
' '
', form.render(), )