test_checkboxselectmultiple.py 9.8 KB

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