summaryrefslogtreecommitdiff
path: root/tests/forms_tests/widget_tests/test_clearablefileinput.py
blob: 4fbaec0910c8b35b9886a0e1ca1bc1375cda77f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
from django.core.files.uploadedfile import SimpleUploadedFile
from django.forms import ClearableFileInput, FileField, Form, MultiWidget

from .base import WidgetTest


class FakeFieldFile:
    """
    Quacks like a FieldFile (has a .url and string representation), but
    doesn't require us to care about storages etc.
    """

    url = "something"

    def __str__(self):
        return self.url


class ClearableFileInputTest(WidgetTest):
    widget = ClearableFileInput()

    def test_clear_input_renders(self):
        """
        A ClearableFileInput with is_required False and rendered with an
        initial value that is a file renders a clear checkbox.
        """
        self.check_html(
            self.widget,
            "myfile",
            FakeFieldFile(),
            html=(
                """
            Currently: <a href="something">something</a>
            <input type="checkbox" name="myfile-clear" id="myfile-clear_id">
            <label for="myfile-clear_id">Clear</label><br>
            Change: <input type="file" name="myfile">
            """
            ),
        )

    def test_html_escaped(self):
        """
        A ClearableFileInput should escape name, filename, and URL
        when rendering HTML (#15182).
        """

        class StrangeFieldFile:
            url = "something?chapter=1&sect=2&copy=3&lang=en"

            def __str__(self):
                return """something<div onclick="alert('oops')">.jpg"""

        self.check_html(
            ClearableFileInput(),
            "my<div>file",
            StrangeFieldFile(),
            html=(
                """
                Currently:
                <a href="something?chapter=1&amp;sect=2&amp;copy=3&amp;lang=en">
                something&lt;div onclick=&quot;alert(&#x27;oops&#x27;)&quot;&gt;.jpg</a>
                <input type="checkbox" name="my&lt;div&gt;file-clear"
                    id="my&lt;div&gt;file-clear_id">
                <label for="my&lt;div&gt;file-clear_id">Clear</label><br>
                Change: <input type="file" name="my&lt;div&gt;file">
                """
            ),
        )

    def test_clear_input_renders_only_if_not_required(self):
        """
        A ClearableFileInput with is_required=True does not render a clear
        checkbox.
        """
        widget = ClearableFileInput()
        widget.is_required = True
        self.check_html(
            widget,
            "myfile",
            FakeFieldFile(),
            html=(
                """
            Currently: <a href="something">something</a> <br>
            Change: <input type="file" name="myfile">
            """
            ),
        )

    def test_clear_input_renders_only_if_initial(self):
        """
        A ClearableFileInput instantiated with no initial value does not render
        a clear checkbox.
        """
        self.check_html(
            self.widget, "myfile", None, html='<input type="file" name="myfile">'
        )

    def test_render_disabled(self):
        self.check_html(
            self.widget,
            "myfile",
            FakeFieldFile(),
            attrs={"disabled": True},
            html=(
                'Currently: <a href="something">something</a>'
                '<input type="checkbox" name="myfile-clear" '
                'id="myfile-clear_id" disabled>'
                '<label for="myfile-clear_id">Clear</label><br>'
                'Change: <input type="file" name="myfile" disabled>'
            ),
        )

    def test_render_no_disabled(self):
        class TestForm(Form):
            clearable_file = FileField(
                widget=self.widget, initial=FakeFieldFile(), required=False
            )

        form = TestForm()
        with self.assertNoLogs("django.template", "DEBUG"):
            form.render()

    def test_render_as_subwidget(self):
        """A ClearableFileInput as a subwidget of MultiWidget."""
        widget = MultiWidget(widgets=(self.widget,))
        self.check_html(
            widget,
            "myfile",
            [FakeFieldFile()],
            html=(
                """
            Currently: <a href="something">something</a>
            <input type="checkbox" name="myfile_0-clear" id="myfile_0-clear_id">
            <label for="myfile_0-clear_id">Clear</label><br>
            Change: <input type="file" name="myfile_0">
            """
            ),
        )

    def test_clear_input_checked_returns_false(self):
        """
        ClearableFileInput.value_from_datadict returns False if the clear
        checkbox is checked, if not required.
        """
        value = self.widget.value_from_datadict(
            data={"myfile-clear": True},
            files={},
            name="myfile",
        )
        self.assertIs(value, False)

    def test_clear_input_checked_returns_false_only_if_not_required(self):
        """
        ClearableFileInput.value_from_datadict never returns False if the field
        is required.
        """
        widget = ClearableFileInput()
        widget.is_required = True
        field = SimpleUploadedFile("something.txt", b"content")

        value = widget.value_from_datadict(
            data={"myfile-clear": True},
            files={"myfile": field},
            name="myfile",
        )
        self.assertEqual(value, field)

    def test_html_does_not_mask_exceptions(self):
        """
        A ClearableFileInput should not mask exceptions produced while
        checking that it has a value.
        """

        class FailingURLFieldFile:
            @property
            def url(self):
                raise ValueError("Canary")

            def __str__(self):
                return "value"

        with self.assertRaisesMessage(ValueError, "Canary"):
            self.widget.render("myfile", FailingURLFieldFile())

    def test_url_as_property(self):
        class URLFieldFile:
            @property
            def url(self):
                return "https://www.python.org/"

            def __str__(self):
                return "value"

        html = self.widget.render("myfile", URLFieldFile())
        self.assertInHTML('<a href="https://www.python.org/">value</a>', html)

    def test_return_false_if_url_does_not_exists(self):
        class NoURLFieldFile:
            def __str__(self):
                return "value"

        html = self.widget.render("myfile", NoURLFieldFile())
        self.assertHTMLEqual(html, '<input name="myfile" type="file">')

    def test_use_required_attribute(self):
        # False when initial data exists. The file input is left blank by the
        # user to keep the existing, initial value.
        self.assertIs(self.widget.use_required_attribute(None), True)
        self.assertIs(self.widget.use_required_attribute("resume.txt"), False)

    def test_value_omitted_from_data(self):
        widget = ClearableFileInput()
        self.assertIs(widget.value_omitted_from_data({}, {}, "field"), True)
        self.assertIs(
            widget.value_omitted_from_data({}, {"field": "x"}, "field"), False
        )
        self.assertIs(
            widget.value_omitted_from_data({"field-clear": "y"}, {}, "field"), False
        )

    def test_fieldset(self):
        class TestForm(Form):
            template_name = "forms_tests/use_fieldset.html"
            field = FileField(widget=self.widget)
            with_file = FileField(widget=self.widget, initial=FakeFieldFile())
            clearable_file = FileField(
                widget=self.widget, initial=FakeFieldFile(), required=False
            )

        form = TestForm()
        self.assertIs(self.widget.use_fieldset, False)
        self.assertHTMLEqual(
            '<div><label for="id_field">Field:</label>'
            '<input id="id_field" name="field" type="file" required></div>'
            '<div><label for="id_with_file">With file:</label>Currently: '
            '<a href="something">something</a><br>Change:<input type="file" '
            'name="with_file" id="id_with_file"></div>'
            '<div><label for="id_clearable_file">Clearable file:</label>'
            'Currently: <a href="something">something</a><input '
            'type="checkbox" name="clearable_file-clear" id="clearable_file-clear_id">'
            '<label for="clearable_file-clear_id">Clear</label><br>Change:'
            '<input type="file" name="clearable_file" id="id_clearable_file"></div>',
            form.render(),
        )