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