1from __future__ import unicode_literals
2
3from unittest import TestCase
4
5from wtforms.form import BaseForm, Form
6from wtforms.meta import DefaultMeta
7from wtforms.fields import TextField, IntegerField
8from wtforms.validators import ValidationError
9from tests.common import DummyPostData
10
11
12class BaseFormTest(TestCase):
13    def get_form(self, **kwargs):
14        def validate_test(form, field):
15            if field.data != 'foobar':
16                raise ValidationError('error')
17
18        return BaseForm({'test': TextField(validators=[validate_test])}, **kwargs)
19
20    def test_data_proxy(self):
21        form = self.get_form()
22        form.process(test='foo')
23        self.assertEqual(form.data, {'test': 'foo'})
24
25    def test_errors_proxy(self):
26        form = self.get_form()
27        form.process(test='foobar')
28        form.validate()
29        self.assertEqual(form.errors, {})
30
31        form = self.get_form()
32        form.process()
33        form.validate()
34        self.assertEqual(form.errors, {'test': ['error']})
35
36    def test_contains(self):
37        form = self.get_form()
38        self.assertTrue('test' in form)
39        self.assertTrue('abcd' not in form)
40
41    def test_field_removal(self):
42        form = self.get_form()
43        del form['test']
44        self.assertRaises(AttributeError, getattr, form, 'test')
45        self.assertTrue('test' not in form)
46
47    def test_field_adding(self):
48        form = self.get_form()
49        self.assertEqual(len(list(form)), 1)
50        form['foo'] = TextField()
51        self.assertEqual(len(list(form)), 2)
52        form.process(DummyPostData(foo=['hello']))
53        self.assertEqual(form['foo'].data, 'hello')
54        form['test'] = IntegerField()
55        self.assertTrue(isinstance(form['test'], IntegerField))
56        self.assertEqual(len(list(form)), 2)
57        self.assertRaises(AttributeError, getattr, form['test'], 'data')
58        form.process(DummyPostData(test=['1']))
59        self.assertEqual(form['test'].data, 1)
60        self.assertEqual(form['foo'].data, '')
61
62    def test_populate_obj(self):
63        m = type(str('Model'), (object, ), {})
64        form = self.get_form()
65        form.process(test='foobar')
66        form.populate_obj(m)
67        self.assertEqual(m.test, 'foobar')
68        self.assertEqual([k for k in dir(m) if not k.startswith('_')], ['test'])
69
70    def test_prefixes(self):
71        form = self.get_form(prefix='foo')
72        self.assertEqual(form['test'].name, 'foo-test')
73        self.assertEqual(form['test'].short_name, 'test')
74        self.assertEqual(form['test'].id, 'foo-test')
75        form = self.get_form(prefix='foo.')
76        form.process(DummyPostData({'foo.test': ['hello'], 'test': ['bye']}))
77        self.assertEqual(form['test'].data, 'hello')
78        self.assertEqual(self.get_form(prefix='foo[')['test'].name, 'foo[-test')
79
80    def test_formdata_wrapper_error(self):
81        form = self.get_form()
82        self.assertRaises(TypeError, form.process, [])
83
84
85class FormMetaTest(TestCase):
86    def test_monkeypatch(self):
87        class F(Form):
88            a = TextField()
89
90        self.assertEqual(F._unbound_fields, None)
91        F()
92        self.assertEqual(F._unbound_fields, [('a', F.a)])
93        F.b = TextField()
94        self.assertEqual(F._unbound_fields, None)
95        F()
96        self.assertEqual(F._unbound_fields, [('a', F.a), ('b', F.b)])
97        del F.a
98        self.assertRaises(AttributeError, lambda: F.a)
99        F()
100        self.assertEqual(F._unbound_fields, [('b', F.b)])
101        F._m = TextField()
102        self.assertEqual(F._unbound_fields, [('b', F.b)])
103
104    def test_subclassing(self):
105        class A(Form):
106            a = TextField()
107            c = TextField()
108
109        class B(A):
110            b = TextField()
111            c = TextField()
112        A()
113        B()
114
115        self.assertTrue(A.a is B.a)
116        self.assertTrue(A.c is not B.c)
117        self.assertEqual(A._unbound_fields, [('a', A.a), ('c', A.c)])
118        self.assertEqual(B._unbound_fields, [('a', B.a), ('b', B.b), ('c', B.c)])
119
120    def test_class_meta_reassign(self):
121        class MetaA:
122            pass
123
124        class MetaB:
125            pass
126
127        class F(Form):
128            Meta = MetaA
129
130        self.assertEqual(F._wtforms_meta, None)
131        assert isinstance(F().meta, MetaA)
132        assert issubclass(F._wtforms_meta, MetaA)
133        F.Meta = MetaB
134        self.assertEqual(F._wtforms_meta, None)
135        assert isinstance(F().meta, MetaB)
136        assert issubclass(F._wtforms_meta, MetaB)
137
138
139class FormTest(TestCase):
140    class F(Form):
141        test = TextField()
142
143        def validate_test(form, field):
144            if field.data != 'foobar':
145                raise ValidationError('error')
146
147    def test_validate(self):
148        form = self.F(test='foobar')
149        self.assertEqual(form.validate(), True)
150
151        form = self.F()
152        self.assertEqual(form.validate(), False)
153
154    def test_field_adding_disabled(self):
155        form = self.F()
156        self.assertRaises(TypeError, form.__setitem__, 'foo', TextField())
157
158    def test_field_removal(self):
159        form = self.F()
160        del form.test
161        self.assertTrue('test' not in form)
162        self.assertEqual(form.test, None)
163        self.assertEqual(len(list(form)), 0)
164        # Try deleting a nonexistent field
165        self.assertRaises(AttributeError, form.__delattr__, 'fake')
166
167    def test_delattr_idempotency(self):
168        form = self.F()
169        del form.test
170        self.assertEqual(form.test, None)
171
172        # Make sure deleting a normal attribute works
173        form.foo = 9
174        del form.foo
175        self.assertRaises(AttributeError, form.__delattr__, 'foo')
176
177        # Check idempotency
178        del form.test
179        self.assertEqual(form.test, None)
180
181    def test_ordered_fields(self):
182        class MyForm(Form):
183            strawberry = TextField()
184            banana = TextField()
185            kiwi = TextField()
186
187        self.assertEqual([x.name for x in MyForm()], ['strawberry', 'banana', 'kiwi'])
188        MyForm.apple = TextField()
189        self.assertEqual([x.name for x in MyForm()], ['strawberry', 'banana', 'kiwi', 'apple'])
190        del MyForm.banana
191        self.assertEqual([x.name for x in MyForm()], ['strawberry', 'kiwi', 'apple'])
192        MyForm.strawberry = TextField()
193        self.assertEqual([x.name for x in MyForm()], ['kiwi', 'apple', 'strawberry'])
194        # Ensure sort is stable: two fields with the same creation counter
195        # should be subsequently sorted by name.
196        MyForm.cherry = MyForm.kiwi
197        self.assertEqual([x.name for x in MyForm()], ['cherry', 'kiwi', 'apple', 'strawberry'])
198
199    def test_data_arg(self):
200        data = {'test': 'foo'}
201        form = self.F(data=data)
202        self.assertEqual(form.test.data, 'foo')
203        form = self.F(data=data, test='bar')
204        self.assertEqual(form.test.data, 'bar')
205
206
207class MetaTest(TestCase):
208    class F(Form):
209        class Meta:
210            foo = 9
211
212        test = TextField()
213
214    class G(Form):
215        class Meta:
216            foo = 12
217            bar = 8
218
219    class H(F, G):
220        class Meta:
221            quux = 42
222
223    class I(F, G):
224        pass
225
226    def test_basic(self):
227        form = self.H()
228        meta = form.meta
229        self.assertEqual(meta.foo, 9)
230        self.assertEqual(meta.bar, 8)
231        self.assertEqual(meta.csrf, False)
232        assert isinstance(meta, self.F.Meta)
233        assert isinstance(meta, self.G.Meta)
234        self.assertEqual(type(meta).__bases__, (
235            self.H.Meta,
236            self.F.Meta,
237            self.G.Meta,
238            DefaultMeta
239        ))
240
241    def test_missing_diamond(self):
242        meta = self.I().meta
243        self.assertEqual(type(meta).__bases__, (
244            self.F.Meta,
245            self.G.Meta,
246            DefaultMeta
247        ))
248