1. import datetime
    
  2. 
    
  3. from django import forms
    
  4. from django.forms import CheckboxSelectMultiple, ChoiceField, Form
    
  5. from django.test import override_settings
    
  6. 
    
  7. from .base import WidgetTest
    
  8. 
    
  9. 
    
  10. class CheckboxSelectMultipleTest(WidgetTest):
    
  11.     widget = CheckboxSelectMultiple
    
  12. 
    
  13.     def test_render_value(self):
    
  14.         self.check_html(
    
  15.             self.widget(choices=self.beatles),
    
  16.             "beatles",
    
  17.             ["J"],
    
  18.             html="""
    
  19.             <div>
    
  20.             <div><label><input checked type="checkbox" name="beatles" value="J"> John
    
  21.             </label></div>
    
  22.             <div><label><input type="checkbox" name="beatles" value="P"> Paul
    
  23.             </label></div>
    
  24.             <div><label><input type="checkbox" name="beatles" value="G"> George
    
  25.             </label></div>
    
  26.             <div><label><input type="checkbox" name="beatles" value="R"> Ringo
    
  27.             </label></div>
    
  28.             </div>
    
  29.         """,
    
  30.         )
    
  31. 
    
  32.     def test_render_value_multiple(self):
    
  33.         self.check_html(
    
  34.             self.widget(choices=self.beatles),
    
  35.             "beatles",
    
  36.             ["J", "P"],
    
  37.             html="""
    
  38.             <div>
    
  39.             <div><label><input checked type="checkbox" name="beatles" value="J"> John
    
  40.             </label></div>
    
  41.             <div><label><input checked type="checkbox" name="beatles" value="P"> Paul
    
  42.             </label></div>
    
  43.             <div><label><input type="checkbox" name="beatles" value="G"> George
    
  44.             </label></div>
    
  45.             <div><label><input type="checkbox" name="beatles" value="R"> Ringo
    
  46.             </label></div>
    
  47.             </div>
    
  48.         """,
    
  49.         )
    
  50. 
    
  51.     def test_render_none(self):
    
  52.         """
    
  53.         If the value is None, none of the options are selected, even if the
    
  54.         choices have an empty option.
    
  55.         """
    
  56.         self.check_html(
    
  57.             self.widget(choices=(("", "Unknown"),) + self.beatles),
    
  58.             "beatles",
    
  59.             None,
    
  60.             html="""
    
  61.             <div>
    
  62.             <div><label><input type="checkbox" name="beatles" value=""> Unknown
    
  63.             </label></div>
    
  64.             <div><label><input type="checkbox" name="beatles" value="J"> John
    
  65.             </label></div>
    
  66.             <div><label><input type="checkbox" name="beatles" value="P"> Paul
    
  67.             </label></div>
    
  68.             <div><label><input type="checkbox" name="beatles" value="G"> George
    
  69.             </label></div>
    
  70.             <div><label><input type="checkbox" name="beatles" value="R"> Ringo
    
  71.             </label></div>
    
  72.             </div>
    
  73.         """,
    
  74.         )
    
  75. 
    
  76.     def test_nested_choices(self):
    
  77.         nested_choices = (
    
  78.             ("unknown", "Unknown"),
    
  79.             ("Audio", (("vinyl", "Vinyl"), ("cd", "CD"))),
    
  80.             ("Video", (("vhs", "VHS"), ("dvd", "DVD"))),
    
  81.         )
    
  82.         html = """
    
  83.         <div id="media">
    
  84.         <div> <label for="media_0">
    
  85.         <input type="checkbox" name="nestchoice" value="unknown" id="media_0"> Unknown
    
  86.         </label></div>
    
  87.         <div>
    
  88.         <label>Audio</label>
    
  89.         <div> <label for="media_1_0">
    
  90.         <input checked type="checkbox" name="nestchoice" value="vinyl" id="media_1_0">
    
  91.         Vinyl</label></div>
    
  92.         <div> <label for="media_1_1">
    
  93.         <input type="checkbox" name="nestchoice" value="cd" id="media_1_1"> CD
    
  94.         </label></div>
    
  95.         </div><div>
    
  96.         <label>Video</label>
    
  97.         <div> <label for="media_2_0">
    
  98.         <input type="checkbox" name="nestchoice" value="vhs" id="media_2_0"> VHS
    
  99.         </label></div>
    
  100.         <div> <label for="media_2_1">
    
  101.         <input type="checkbox" name="nestchoice" value="dvd" id="media_2_1" checked> DVD
    
  102.         </label></div>
    
  103.         </div>
    
  104.         </div>
    
  105.         """
    
  106.         self.check_html(
    
  107.             self.widget(choices=nested_choices),
    
  108.             "nestchoice",
    
  109.             ("vinyl", "dvd"),
    
  110.             attrs={"id": "media"},
    
  111.             html=html,
    
  112.         )
    
  113. 
    
  114.     def test_nested_choices_without_id(self):
    
  115.         nested_choices = (
    
  116.             ("unknown", "Unknown"),
    
  117.             ("Audio", (("vinyl", "Vinyl"), ("cd", "CD"))),
    
  118.             ("Video", (("vhs", "VHS"), ("dvd", "DVD"))),
    
  119.         )
    
  120.         html = """
    
  121.         <div>
    
  122.         <div> <label>
    
  123.         <input type="checkbox" name="nestchoice" value="unknown"> Unknown</label></div>
    
  124.         <div>
    
  125.         <label>Audio</label>
    
  126.         <div> <label>
    
  127.         <input checked type="checkbox" name="nestchoice" value="vinyl"> Vinyl
    
  128.         </label></div>
    
  129.         <div> <label>
    
  130.         <input type="checkbox" name="nestchoice" value="cd"> CD</label></div>
    
  131.         </div><div>
    
  132.         <label>Video</label>
    
  133.         <div> <label>
    
  134.         <input type="checkbox" name="nestchoice" value="vhs"> VHS</label></div>
    
  135.         <div> <label>
    
  136.         <input type="checkbox" name="nestchoice" value="dvd"checked> DVD</label></div>
    
  137.         </div>
    
  138.         </div>
    
  139.         """
    
  140.         self.check_html(
    
  141.             self.widget(choices=nested_choices),
    
  142.             "nestchoice",
    
  143.             ("vinyl", "dvd"),
    
  144.             html=html,
    
  145.         )
    
  146. 
    
  147.     def test_separate_ids(self):
    
  148.         """
    
  149.         Each input gets a separate ID.
    
  150.         """
    
  151.         choices = [("a", "A"), ("b", "B"), ("c", "C")]
    
  152.         html = """
    
  153.         <div id="abc">
    
  154.         <div>
    
  155.         <label for="abc_0">
    
  156.         <input checked type="checkbox" name="letters" value="a" id="abc_0"> A</label>
    
  157.         </div>
    
  158.         <div><label for="abc_1">
    
  159.         <input type="checkbox" name="letters" value="b" id="abc_1"> B</label></div>
    
  160.         <div>
    
  161.         <label for="abc_2">
    
  162.         <input checked type="checkbox" name="letters" value="c" id="abc_2"> C</label>
    
  163.         </div>
    
  164.         </div>
    
  165.         """
    
  166.         self.check_html(
    
  167.             self.widget(choices=choices),
    
  168.             "letters",
    
  169.             ["a", "c"],
    
  170.             attrs={"id": "abc"},
    
  171.             html=html,
    
  172.         )
    
  173. 
    
  174.     def test_separate_ids_constructor(self):
    
  175.         """
    
  176.         Each input gets a separate ID when the ID is passed to the constructor.
    
  177.         """
    
  178.         widget = CheckboxSelectMultiple(
    
  179.             attrs={"id": "abc"}, choices=[("a", "A"), ("b", "B"), ("c", "C")]
    
  180.         )
    
  181.         html = """
    
  182.         <div id="abc">
    
  183.         <div>
    
  184.         <label for="abc_0">
    
  185.         <input checked type="checkbox" name="letters" value="a" id="abc_0"> A</label>
    
  186.         </div>
    
  187.         <div><label for="abc_1">
    
  188.         <input type="checkbox" name="letters" value="b" id="abc_1"> B</label></div>
    
  189.         <div>
    
  190.         <label for="abc_2">
    
  191.         <input checked type="checkbox" name="letters" value="c" id="abc_2"> C</label>
    
  192.         </div>
    
  193.         </div>
    
  194.         """
    
  195.         self.check_html(widget, "letters", ["a", "c"], html=html)
    
  196. 
    
  197.     @override_settings(USE_THOUSAND_SEPARATOR=True)
    
  198.     def test_doesnt_localize_input_value(self):
    
  199.         choices = [
    
  200.             (1, "One"),
    
  201.             (1000, "One thousand"),
    
  202.             (1000000, "One million"),
    
  203.         ]
    
  204.         html = """
    
  205.         <div>
    
  206.         <div><label><input type="checkbox" name="numbers" value="1"> One</label></div>
    
  207.         <div><label>
    
  208.         <input type="checkbox" name="numbers" value="1000"> One thousand</label></div>
    
  209.         <div><label>
    
  210.         <input type="checkbox" name="numbers" value="1000000"> One million</label></div>
    
  211.         </div>
    
  212.         """
    
  213.         self.check_html(self.widget(choices=choices), "numbers", None, html=html)
    
  214. 
    
  215.         choices = [
    
  216.             (datetime.time(0, 0), "midnight"),
    
  217.             (datetime.time(12, 0), "noon"),
    
  218.         ]
    
  219.         html = """
    
  220.         <div>
    
  221.         <div><label>
    
  222.         <input type="checkbox" name="times" value="00:00:00"> midnight</label></div>
    
  223.         <div><label>
    
  224.         <input type="checkbox" name="times" value="12:00:00"> noon</label></div>
    
  225.         </div>
    
  226.         """
    
  227.         self.check_html(self.widget(choices=choices), "times", None, html=html)
    
  228. 
    
  229.     def test_use_required_attribute(self):
    
  230.         widget = self.widget(choices=self.beatles)
    
  231.         # Always False because browser validation would require all checkboxes
    
  232.         # to be checked instead of at least one.
    
  233.         self.assertIs(widget.use_required_attribute(None), False)
    
  234.         self.assertIs(widget.use_required_attribute([]), False)
    
  235.         self.assertIs(widget.use_required_attribute(["J", "P"]), False)
    
  236. 
    
  237.     def test_value_omitted_from_data(self):
    
  238.         widget = self.widget(choices=self.beatles)
    
  239.         self.assertIs(widget.value_omitted_from_data({}, {}, "field"), False)
    
  240.         self.assertIs(
    
  241.             widget.value_omitted_from_data({"field": "value"}, {}, "field"), False
    
  242.         )
    
  243. 
    
  244.     def test_label(self):
    
  245.         """
    
  246.         CheckboxSelectMultiple doesn't contain 'for="field_0"' in the <label>
    
  247.         because clicking that would toggle the first checkbox.
    
  248.         """
    
  249. 
    
  250.         class TestForm(forms.Form):
    
  251.             f = forms.MultipleChoiceField(widget=CheckboxSelectMultiple)
    
  252. 
    
  253.         bound_field = TestForm()["f"]
    
  254.         self.assertEqual(bound_field.field.widget.id_for_label("id"), "")
    
  255.         self.assertEqual(bound_field.label_tag(), "<label>F:</label>")
    
  256.         self.assertEqual(bound_field.legend_tag(), "<legend>F:</legend>")
    
  257. 
    
  258.     def test_fieldset(self):
    
  259.         class TestForm(Form):
    
  260.             template_name = "forms_tests/use_fieldset.html"
    
  261.             field = ChoiceField(widget=self.widget, choices=self.beatles)
    
  262. 
    
  263.         form = TestForm()
    
  264.         self.assertIs(self.widget.use_fieldset, True)
    
  265.         self.assertHTMLEqual(
    
  266.             form.render(),
    
  267.             '<div><fieldset><legend>Field:</legend><div id="id_field">'
    
  268.             '<div><label for="id_field_0"><input type="checkbox" '
    
  269.             'name="field" value="J" id="id_field_0"> John</label></div>'
    
  270.             '<div><label for="id_field_1"><input type="checkbox" '
    
  271.             'name="field" value="P" id="id_field_1">Paul</label></div>'
    
  272.             '<div><label for="id_field_2"><input type="checkbox" '
    
  273.             'name="field" value="G" id="id_field_2"> George</label></div>'
    
  274.             '<div><label for="id_field_3"><input type="checkbox" '
    
  275.             'name="field" value="R" id="id_field_3">'
    
  276.             "Ringo</label></div></div></fieldset></div>",
    
  277.         )