Add form input fields utility
[flowspy] / widget_tweaks / tests.py
1 import string
2 try:
3     from django.utils.unittest import expectedFailure
4 except ImportError:
5     def expectedFailure(func):
6         return lambda *args, **kwargs: None
7
8 from django.test import TestCase
9 from django.forms import Form, CharField, TextInput
10 from django import forms
11 from django.template import Template, Context
12 from django.forms.extras.widgets import SelectDateWidget
13
14 # ==============================
15 #       Testing helpers
16 # ==============================
17
18 class MyForm(Form):
19     """
20     Test form. If you want to test rendering of a field,
21     add it to this form and use one of 'render_...' functions
22     from this module.
23     """
24     simple = CharField()
25     with_attrs = CharField(widget=TextInput(attrs={
26                     'foo': 'baz',
27                     'egg': 'spam'
28                  }))
29     with_cls = CharField(widget=TextInput(attrs={'class':'class0'}))
30     date = forms.DateField(widget=SelectDateWidget(attrs={'egg': 'spam'}))
31
32
33 def render_form(text, form=None, **context_args):
34     """
35     Renders template ``text`` with widget_tweaks library loaded
36     and MyForm instance available in context as ``form``.
37     """
38     tpl = Template("{% load widget_tweaks %}" + text)
39     context_args.update({'form': MyForm() if form is None else form})
40     context = Context(context_args)
41     return tpl.render(context)
42
43
44 def render_field(field, template_filter, params, *args, **kwargs):
45     """
46     Renders ``field`` of MyForm with filter ``template_filter`` applied.
47     ``params`` are filter arguments.
48
49     If you want to apply several filters (in a chain),
50     pass extra ``template_filter`` and ``params`` as positional arguments.
51
52     In order to use custom form, pass form instance as ``form``
53     keyword argument.
54     """
55     filters = [(template_filter, params)]
56     filters.extend(zip(args[::2], args[1::2]))
57     filter_strings = ['|%s:"%s"' % (f[0], f[1],) for f in filters]
58     render_field_str = '{{ form.%s%s }}' % (field, ''.join(filter_strings))
59     return render_form(render_field_str, **kwargs)
60
61
62 def render_field_from_tag(field, *attributes):
63     """
64     Renders MyForm's field ``field`` with attributes passed
65     as positional arguments.
66     """
67     attr_strings = [' %s' % f for f in attributes]
68     tpl = string.Template('{% render_field form.$field$attrs %}')
69     render_field_str = tpl.substitute(field=field, attrs=''.join(attr_strings))
70     return render_form(render_field_str)
71
72
73 def assertIn(value, obj):
74     assert value in obj, "%s not in %s" % (value, obj,)
75
76
77 def assertNotIn(value, obj):
78     assert value not in obj, "%s in %s" % (value, obj,)
79
80
81 # ===============================
82 #           Test cases
83 # ===============================
84
85 class SimpleAttrTest(TestCase):
86     def test_attr(self):
87         res = render_field('simple', 'attr', 'foo:bar')
88         assertIn('type="text"', res)
89         assertIn('name="simple"', res)
90         assertIn('id="id_simple"', res)
91         assertIn('foo="bar"', res)
92
93     def test_attr_chaining(self):
94         res = render_field('simple', 'attr', 'foo:bar', 'attr', 'bar:baz')
95         assertIn('type="text"', res)
96         assertIn('name="simple"', res)
97         assertIn('id="id_simple"', res)
98         assertIn('foo="bar"', res)
99         assertIn('bar="baz"', res)
100
101     def test_add_class(self):
102         res = render_field('simple', 'add_class', 'foo')
103         assertIn('class="foo"', res)
104
105     def test_add_multiple_classes(self):
106         res = render_field('simple', 'add_class', 'foo bar')
107         assertIn('class="foo bar"', res)
108
109     def test_add_class_chaining(self):
110         res = render_field('simple', 'add_class', 'foo', 'add_class', 'bar')
111         assertIn('class="bar foo"', res)
112
113     def test_set_data(self):
114         res = render_field('simple', 'set_data', 'key:value')
115         assertIn('data-key="value"', res)
116
117
118 class ErrorsTest(TestCase):
119
120     def _err_form(self):
121         form = MyForm({'foo': 'bar'})  # some random data
122         form.is_valid()  # trigger form validation
123         return form
124
125     def test_error_class_no_error(self):
126         res = render_field('simple', 'add_error_class', 'err')
127         assertNotIn('class="err"', res)
128
129     def test_error_class_error(self):
130         form = self._err_form()
131         res = render_field('simple', 'add_error_class', 'err', form=form)
132         assertIn('class="err"', res)
133
134     def test_error_attr_no_error(self):
135         res = render_field('simple', 'add_error_attr', 'aria-invalid:true')
136         assertNotIn('aria-invalid="true"', res)
137
138     def test_error_attr_error(self):
139         form = self._err_form()
140         res = render_field('simple', 'add_error_attr', 'aria-invalid:true', form=form)
141         assertIn('aria-invalid="true"', res)
142
143
144 class SilenceTest(TestCase):
145     def test_silence_without_field(self):
146         res = render_field("nothing", 'attr', 'foo:bar')
147         self.assertEqual(res, "")
148         res = render_field("nothing", 'add_class', 'some')
149         self.assertEqual(res, "")
150
151
152 class CustomizedWidgetTest(TestCase):
153     def test_attr(self):
154         res = render_field('with_attrs', 'attr', 'foo:bar')
155         assertIn('foo="bar"', res)
156         assertNotIn('foo="baz"', res)
157         assertIn('egg="spam"', res)
158
159     # see https://code.djangoproject.com/ticket/16754
160     @expectedFailure
161     def test_selectdatewidget(self):
162         res = render_field('date', 'attr', 'foo:bar')
163         assertIn('egg="spam"', res)
164         assertIn('foo="bar"', res)
165
166     def test_attr_chaining(self):
167         res = render_field('with_attrs', 'attr', 'foo:bar', 'attr', 'bar:baz')
168         assertIn('foo="bar"', res)
169         assertNotIn('foo="baz"', res)
170         assertIn('egg="spam"', res)
171         assertIn('bar="baz"', res)
172
173     def test_attr_class(self):
174         res = render_field('with_cls', 'attr', 'foo:bar')
175         assertIn('foo="bar"', res)
176         assertIn('class="class0"', res)
177
178     def test_default_attr(self):
179         res = render_field('with_cls', 'attr', 'type:search')
180         assertIn('class="class0"', res)
181         assertIn('type="search"', res)
182
183     def test_add_class(self):
184         res = render_field('with_cls', 'add_class', 'class1')
185         assertIn('class0', res)
186         assertIn('class1', res)
187
188     def test_add_class_chaining(self):
189         res = render_field('with_cls', 'add_class', 'class1', 'add_class', 'class2')
190         assertIn('class0', res)
191         assertIn('class1', res)
192         assertIn('class2', res)
193
194
195 class FieldReuseTest(TestCase):
196
197     def test_field_double_rendering_simple(self):
198         res = render_form('{{ form.simple }}{{ form.simple|attr:"foo:bar" }}{{ form.simple }}')
199         self.assertEqual(res.count("bar"), 1)
200
201     def test_field_double_rendering_simple_css(self):
202         res = render_form('{{ form.simple }}{{ form.simple|add_class:"bar" }}{{ form.simple|add_class:"baz" }}')
203         self.assertEqual(res.count("baz"), 1)
204         self.assertEqual(res.count("bar"), 1)
205
206     def test_field_double_rendering_attrs(self):
207         res = render_form('{{ form.with_cls }}{{ form.with_cls|add_class:"bar" }}{{ form.with_cls }}')
208         self.assertEqual(res.count("class0"), 3)
209         self.assertEqual(res.count("bar"), 1)
210
211
212 class SimpleRenderFieldTagTest(TestCase):
213     def test_attr(self):
214         res = render_field_from_tag('simple', 'foo="bar"')
215         assertIn('type="text"', res)
216         assertIn('name="simple"', res)
217         assertIn('id="id_simple"', res)
218         assertIn('foo="bar"', res)
219
220     def test_multiple_attrs(self):
221         res = render_field_from_tag('simple', 'foo="bar"', 'bar="baz"')
222         assertIn('type="text"', res)
223         assertIn('name="simple"', res)
224         assertIn('id="id_simple"', res)
225         assertIn('foo="bar"', res)
226         assertIn('bar="baz"', res)
227
228
229 class RenderFieldTagSilenceTest(TestCase):
230     def test_silence_without_field(self):
231         res = render_field_from_tag("nothing", 'foo="bar"')
232         self.assertEqual(res, "")
233         res = render_field_from_tag("nothing", 'class+="some"')
234         self.assertEqual(res, "")
235
236
237 class RenderFieldTagCustomizedWidgetTest(TestCase):
238     def test_attr(self):
239         res = render_field_from_tag('with_attrs', 'foo="bar"')
240         assertIn('foo="bar"', res)
241         assertNotIn('foo="baz"', res)
242         assertIn('egg="spam"', res)
243
244     # see https://code.djangoproject.com/ticket/16754
245     @expectedFailure
246     def test_selectdatewidget(self):
247         res = render_field_from_tag('date', 'foo="bar"')
248         assertIn('egg="spam"', res)
249         assertIn('foo="bar"', res)
250
251     def test_multiple_attrs(self):
252         res = render_field_from_tag('with_attrs', 'foo="bar"', 'bar="baz"')
253         assertIn('foo="bar"', res)
254         assertNotIn('foo="baz"', res)
255         assertIn('egg="spam"', res)
256         assertIn('bar="baz"', res)
257
258     def test_attr_class(self):
259         res = render_field_from_tag('with_cls', 'foo="bar"')
260         assertIn('foo="bar"', res)
261         assertIn('class="class0"', res)
262
263     def test_default_attr(self):
264         res = render_field_from_tag('with_cls', 'type="search"')
265         assertIn('class="class0"', res)
266         assertIn('type="search"', res)
267
268     def test_append_attr(self):
269         res = render_field_from_tag('with_cls', 'class+="class1"')
270         assertIn('class0', res)
271         assertIn('class1', res)
272
273     def test_duplicate_append_attr(self):
274         res = render_field_from_tag('with_cls', 'class+="class1"', 'class+="class2"')
275         assertIn('class0', res)
276         assertIn('class1', res)
277         assertIn('class2', res)
278
279     def test_hyphenated_attributes(self):
280         res = render_field_from_tag('with_cls', 'data-foo="bar"')
281         assertIn('data-foo="bar"', res)
282         assertIn('class="class0"', res)
283
284
285 class RenderFieldWidgetClassesTest(TestCase):
286     def test_use_widget_required_class(self):
287         res = render_form('{% render_field form.simple %}',
288                           WIDGET_REQUIRED_CLASS='required_class')
289         assertIn('class="required_class"', res)
290
291     def test_use_widget_error_class(self):
292         res = render_form('{% render_field form.simple %}', form=MyForm({}),
293                           WIDGET_ERROR_CLASS='error_class')
294         assertIn('class="error_class"', res)
295
296     def test_use_widget_error_class_with_other_classes(self):
297         res = render_form('{% render_field form.simple class="blue" %}',
298                           form=MyForm({}), WIDGET_ERROR_CLASS='error_class')
299         assertIn('class="blue error_class"', res)
300
301     def test_use_widget_required_class_with_other_classes(self):
302         res = render_form('{% render_field form.simple class="blue" %}',
303                           form=MyForm({}), WIDGET_REQUIRED_CLASS='required_class')
304         assertIn('class="blue required_class"', res)
305
306
307 class RenderFieldTagFieldReuseTest(TestCase):
308     def test_field_double_rendering_simple(self):
309         res = render_form('{{ form.simple }}{% render_field form.simple foo="bar" %}{% render_field form.simple %}')
310         self.assertEqual(res.count("bar"), 1)
311
312     def test_field_double_rendering_simple_css(self):
313         res = render_form('{% render_field form.simple %}{% render_field form.simple class+="bar" %}{% render_field form.simple class+="baz" %}')
314         self.assertEqual(res.count("baz"), 1)
315         self.assertEqual(res.count("bar"), 1)
316
317     def test_field_double_rendering_attrs(self):
318         res = render_form('{% render_field form.with_cls %}{% render_field form.with_cls class+="bar" %}{% render_field form.with_cls %}')
319         self.assertEqual(res.count("class0"), 3)
320         self.assertEqual(res.count("bar"), 1)
321
322
323 class RenderFieldTagUseTemplateVariableTest(TestCase):
324     def test_use_template_variable_in_parametrs(self):
325         res = render_form('{% render_field form.with_attrs egg+="pahaz" placeholder=form.with_attrs.label %}')
326         assertIn('egg="spam pahaz"', res)
327         assertIn('placeholder="With attrs"', res)
328
329
330 class RenderFieldFilter_field_type_widget_type_Test(TestCase):
331     def test_field_type_widget_type_rendering_simple(self):
332         res = render_form('<div class="{{ form.simple|field_type }} {{ form.simple|widget_type }} {{ form.simple.html_name }}">{{ form.simple }}</div>')
333         assertIn('class="charfield textinput simple"', res)