test_clearablefileinput.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. from django.core.files.uploadedfile import SimpleUploadedFile
  2. from django.forms import ClearableFileInput, FileField, Form, MultiWidget
  3. from .base import WidgetTest
  4. class FakeFieldFile:
  5. """
  6. Quacks like a FieldFile (has a .url and string representation), but
  7. doesn't require us to care about storages etc.
  8. """
  9. url = "something"
  10. def __str__(self):
  11. return self.url
  12. class ClearableFileInputTest(WidgetTest):
  13. widget = ClearableFileInput()
  14. def test_clear_input_renders(self):
  15. """
  16. A ClearableFileInput with is_required False and rendered with an
  17. initial value that is a file renders a clear checkbox.
  18. """
  19. self.check_html(
  20. self.widget,
  21. "myfile",
  22. FakeFieldFile(),
  23. html=(
  24. """
  25. Currently: <a href="something">something</a>
  26. <input type="checkbox" name="myfile-clear" id="myfile-clear_id">
  27. <label for="myfile-clear_id">Clear</label><br>
  28. Change: <input type="file" name="myfile">
  29. """
  30. ),
  31. )
  32. def test_html_escaped(self):
  33. """
  34. A ClearableFileInput should escape name, filename, and URL
  35. when rendering HTML (#15182).
  36. """
  37. class StrangeFieldFile:
  38. url = "something?chapter=1&sect=2&copy=3&lang=en"
  39. def __str__(self):
  40. return """something<div onclick="alert('oops')">.jpg"""
  41. self.check_html(
  42. ClearableFileInput(),
  43. "my<div>file",
  44. StrangeFieldFile(),
  45. html=(
  46. """
  47. Currently:
  48. <a href="something?chapter=1&amp;sect=2&amp;copy=3&amp;lang=en">
  49. something&lt;div onclick=&quot;alert(&#x27;oops&#x27;)&quot;&gt;.jpg</a>
  50. <input type="checkbox" name="my&lt;div&gt;file-clear"
  51. id="my&lt;div&gt;file-clear_id">
  52. <label for="my&lt;div&gt;file-clear_id">Clear</label><br>
  53. Change: <input type="file" name="my&lt;div&gt;file">
  54. """
  55. ),
  56. )
  57. def test_clear_input_renders_only_if_not_required(self):
  58. """
  59. A ClearableFileInput with is_required=True does not render a clear
  60. checkbox.
  61. """
  62. widget = ClearableFileInput()
  63. widget.is_required = True
  64. self.check_html(
  65. widget,
  66. "myfile",
  67. FakeFieldFile(),
  68. html=(
  69. """
  70. Currently: <a href="something">something</a> <br>
  71. Change: <input type="file" name="myfile">
  72. """
  73. ),
  74. )
  75. def test_clear_input_renders_only_if_initial(self):
  76. """
  77. A ClearableFileInput instantiated with no initial value does not render
  78. a clear checkbox.
  79. """
  80. self.check_html(
  81. self.widget, "myfile", None, html='<input type="file" name="myfile">'
  82. )
  83. def test_render_disabled(self):
  84. self.check_html(
  85. self.widget,
  86. "myfile",
  87. FakeFieldFile(),
  88. attrs={"disabled": True},
  89. html=(
  90. 'Currently: <a href="something">something</a>'
  91. '<input type="checkbox" name="myfile-clear" '
  92. 'id="myfile-clear_id" disabled>'
  93. '<label for="myfile-clear_id">Clear</label><br>'
  94. 'Change: <input type="file" name="myfile" disabled>'
  95. ),
  96. )
  97. def test_render_as_subwidget(self):
  98. """A ClearableFileInput as a subwidget of MultiWidget."""
  99. widget = MultiWidget(widgets=(self.widget,))
  100. self.check_html(
  101. widget,
  102. "myfile",
  103. [FakeFieldFile()],
  104. html=(
  105. """
  106. Currently: <a href="something">something</a>
  107. <input type="checkbox" name="myfile_0-clear" id="myfile_0-clear_id">
  108. <label for="myfile_0-clear_id">Clear</label><br>
  109. Change: <input type="file" name="myfile_0">
  110. """
  111. ),
  112. )
  113. def test_clear_input_checked_returns_false(self):
  114. """
  115. ClearableFileInput.value_from_datadict returns False if the clear
  116. checkbox is checked, if not required.
  117. """
  118. value = self.widget.value_from_datadict(
  119. data={"myfile-clear": True},
  120. files={},
  121. name="myfile",
  122. )
  123. self.assertIs(value, False)
  124. def test_clear_input_checked_returns_false_only_if_not_required(self):
  125. """
  126. ClearableFileInput.value_from_datadict never returns False if the field
  127. is required.
  128. """
  129. widget = ClearableFileInput()
  130. widget.is_required = True
  131. field = SimpleUploadedFile("something.txt", b"content")
  132. value = widget.value_from_datadict(
  133. data={"myfile-clear": True},
  134. files={"myfile": field},
  135. name="myfile",
  136. )
  137. self.assertEqual(value, field)
  138. def test_html_does_not_mask_exceptions(self):
  139. """
  140. A ClearableFileInput should not mask exceptions produced while
  141. checking that it has a value.
  142. """
  143. class FailingURLFieldFile:
  144. @property
  145. def url(self):
  146. raise ValueError("Canary")
  147. def __str__(self):
  148. return "value"
  149. with self.assertRaisesMessage(ValueError, "Canary"):
  150. self.widget.render("myfile", FailingURLFieldFile())
  151. def test_url_as_property(self):
  152. class URLFieldFile:
  153. @property
  154. def url(self):
  155. return "https://www.python.org/"
  156. def __str__(self):
  157. return "value"
  158. html = self.widget.render("myfile", URLFieldFile())
  159. self.assertInHTML('<a href="https://www.python.org/">value</a>', html)
  160. def test_return_false_if_url_does_not_exists(self):
  161. class NoURLFieldFile:
  162. def __str__(self):
  163. return "value"
  164. html = self.widget.render("myfile", NoURLFieldFile())
  165. self.assertHTMLEqual(html, '<input name="myfile" type="file">')
  166. def test_use_required_attribute(self):
  167. # False when initial data exists. The file input is left blank by the
  168. # user to keep the existing, initial value.
  169. self.assertIs(self.widget.use_required_attribute(None), True)
  170. self.assertIs(self.widget.use_required_attribute("resume.txt"), False)
  171. def test_value_omitted_from_data(self):
  172. widget = ClearableFileInput()
  173. self.assertIs(widget.value_omitted_from_data({}, {}, "field"), True)
  174. self.assertIs(
  175. widget.value_omitted_from_data({}, {"field": "x"}, "field"), False
  176. )
  177. self.assertIs(
  178. widget.value_omitted_from_data({"field-clear": "y"}, {}, "field"), False
  179. )
  180. def test_fieldset(self):
  181. class TestForm(Form):
  182. template_name = "forms_tests/use_fieldset.html"
  183. field = FileField(widget=self.widget)
  184. with_file = FileField(widget=self.widget, initial=FakeFieldFile())
  185. clearable_file = FileField(
  186. widget=self.widget, initial=FakeFieldFile(), required=False
  187. )
  188. form = TestForm()
  189. self.assertIs(self.widget.use_fieldset, False)
  190. self.assertHTMLEqual(
  191. '<div><label for="id_field">Field:</label>'
  192. '<input id="id_field" name="field" type="file" required></div>'
  193. '<div><label for="id_with_file">With file:</label>Currently: '
  194. '<a href="something">something</a><br>Change:<input type="file" '
  195. 'name="with_file" id="id_with_file"></div>'
  196. '<div><label for="id_clearable_file">Clearable file:</label>'
  197. 'Currently: <a href="something">something</a><input '
  198. 'type="checkbox" name="clearable_file-clear" id="clearable_file-clear_id">'
  199. '<label for="clearable_file-clear_id">Clear</label><br>Change:'
  200. '<input type="file" name="clearable_file" id="id_clearable_file"></div>',
  201. form.render(),
  202. )