1from __future__ import unicode_literals 2 3import sys 4 5from datetime import date, datetime 6from decimal import Decimal, ROUND_UP, ROUND_DOWN 7from unittest import TestCase 8 9from wtforms import validators, widgets, meta 10from wtforms.fields import * 11from wtforms.fields import Label, Field, SelectFieldBase, html5 12from wtforms.form import Form 13from wtforms.compat import text_type 14from wtforms.utils import unset_value 15from tests.common import DummyPostData 16 17PYTHON_VERSION = sys.version_info 18 19 20class AttrDict(object): 21 def __init__(self, *args, **kw): 22 self.__dict__.update(*args, **kw) 23 24 25def make_form(_name='F', **fields): 26 return type(str(_name), (Form, ), fields) 27 28 29class DefaultsTest(TestCase): 30 def test(self): 31 expected = 42 32 33 def default_callable(): 34 return expected 35 36 test_value = TextField(default=expected).bind(Form(), 'a') 37 test_value.process(None) 38 self.assertEqual(test_value.data, expected) 39 40 test_callable = TextField(default=default_callable).bind(Form(), 'a') 41 test_callable.process(None) 42 self.assertEqual(test_callable.data, expected) 43 44 45class LabelTest(TestCase): 46 def test(self): 47 expected = """<label for="test">Caption</label>""" 48 label = Label('test', 'Caption') 49 self.assertEqual(label(), expected) 50 self.assertEqual(str(label), expected) 51 self.assertEqual(text_type(label), expected) 52 self.assertEqual(label.__html__(), expected) 53 self.assertEqual(label().__html__(), expected) 54 self.assertEqual(label('hello'), """<label for="test">hello</label>""") 55 self.assertEqual(TextField('hi').bind(Form(), 'a').label.text, 'hi') 56 if PYTHON_VERSION < (3,): 57 self.assertEqual(repr(label), "Label(u'test', u'Caption')") 58 else: 59 self.assertEqual(repr(label), "Label('test', 'Caption')") 60 self.assertEqual(label.__unicode__(), expected) 61 62 def test_auto_label(self): 63 t1 = TextField().bind(Form(), 'foo_bar') 64 self.assertEqual(t1.label.text, 'Foo Bar') 65 66 t2 = TextField('').bind(Form(), 'foo_bar') 67 self.assertEqual(t2.label.text, '') 68 69 def test_override_for(self): 70 label = Label('test', 'Caption') 71 self.assertEqual(label(for_='foo'), """<label for="foo">Caption</label>""") 72 self.assertEqual(label(**{'for': 'bar'}), """<label for="bar">Caption</label>""") 73 74 75class FlagsTest(TestCase): 76 def setUp(self): 77 t = TextField(validators=[validators.Required()]).bind(Form(), 'a') 78 self.flags = t.flags 79 80 def test_existing_values(self): 81 self.assertEqual(self.flags.required, True) 82 self.assertTrue('required' in self.flags) 83 self.assertEqual(self.flags.optional, False) 84 self.assertTrue('optional' not in self.flags) 85 86 def test_assignment(self): 87 self.assertTrue('optional' not in self.flags) 88 self.flags.optional = True 89 self.assertEqual(self.flags.optional, True) 90 self.assertTrue('optional' in self.flags) 91 92 def test_unset(self): 93 self.flags.required = False 94 self.assertEqual(self.flags.required, False) 95 self.assertTrue('required' not in self.flags) 96 97 def test_repr(self): 98 self.assertEqual(repr(self.flags), '<wtforms.fields.Flags: {required}>') 99 100 def test_underscore_property(self): 101 self.assertRaises(AttributeError, getattr, self.flags, '_foo') 102 self.flags._foo = 42 103 self.assertEqual(self.flags._foo, 42) 104 105 106class UnsetValueTest(TestCase): 107 def test(self): 108 self.assertEqual(str(unset_value), '<unset value>') 109 self.assertEqual(repr(unset_value), '<unset value>') 110 self.assertEqual(bool(unset_value), False) 111 assert not unset_value 112 self.assertEqual(unset_value.__nonzero__(), False) 113 self.assertEqual(unset_value.__bool__(), False) 114 115 116class FiltersTest(TestCase): 117 class F(Form): 118 a = TextField(default=' hello', filters=[lambda x: x.strip()]) 119 b = TextField(default='42', filters=[int, lambda x: -x]) 120 121 def test_working(self): 122 form = self.F() 123 self.assertEqual(form.a.data, 'hello') 124 self.assertEqual(form.b.data, -42) 125 assert form.validate() 126 127 def test_failure(self): 128 form = self.F(DummyPostData(a=[' foo bar '], b=['hi'])) 129 self.assertEqual(form.a.data, 'foo bar') 130 self.assertEqual(form.b.data, 'hi') 131 self.assertEqual(len(form.b.process_errors), 1) 132 assert not form.validate() 133 134 135class FieldTest(TestCase): 136 class F(Form): 137 a = TextField(default='hello', render_kw={'readonly': True, 'foo': u'bar'}) 138 139 def setUp(self): 140 self.field = self.F().a 141 142 def test_unbound_field(self): 143 unbound = self.F.a 144 assert unbound.creation_counter != 0 145 assert unbound.field_class is TextField 146 self.assertEqual(unbound.args, ()) 147 self.assertEqual(unbound.kwargs, {'default': 'hello', 'render_kw': {'readonly': True, 'foo': u'bar'}}) 148 assert repr(unbound).startswith('<UnboundField(TextField') 149 150 def test_htmlstring(self): 151 self.assertTrue(isinstance(self.field.__html__(), widgets.HTMLString)) 152 153 def test_str_coerce(self): 154 self.assertTrue(isinstance(str(self.field), str)) 155 self.assertEqual(str(self.field), str(self.field())) 156 157 def test_unicode_coerce(self): 158 self.assertEqual(text_type(self.field), self.field()) 159 160 def test_process_formdata(self): 161 Field.process_formdata(self.field, [42]) 162 self.assertEqual(self.field.data, 42) 163 164 def test_meta_attribute(self): 165 # Can we pass in meta via _form? 166 form = self.F() 167 assert form.a.meta is form.meta 168 169 # Can we pass in meta via _meta? 170 form_meta = meta.DefaultMeta() 171 field = TextField(_name='Foo', _form=None, _meta=form_meta) 172 assert field.meta is form_meta 173 174 # Do we fail if both _meta and _form are None? 175 self.assertRaises(TypeError, TextField, _name='foo', _form=None) 176 177 def test_render_kw(self): 178 form = self.F() 179 self.assertEqual(form.a(), u'<input foo="bar" id="a" name="a" readonly type="text" value="hello">') 180 self.assertEqual(form.a(foo=u'baz'), u'<input foo="baz" id="a" name="a" readonly type="text" value="hello">') 181 self.assertEqual( 182 form.a(foo=u'baz', readonly=False, other='hello'), 183 u'<input foo="baz" id="a" name="a" other="hello" type="text" value="hello">' 184 ) 185 186 187class PrePostTestField(TextField): 188 def pre_validate(self, form): 189 if self.data == "stoponly": 190 raise validators.StopValidation() 191 elif self.data.startswith("stop"): 192 raise validators.StopValidation("stop with message") 193 194 def post_validate(self, form, stopped): 195 if self.data == "p": 196 raise ValueError("Post") 197 elif stopped and self.data == "stop-post": 198 raise ValueError("Post-stopped") 199 200 201class PrePostValidationTest(TestCase): 202 class F(Form): 203 a = PrePostTestField(validators=[validators.Length(max=1, message="too long")]) 204 205 def _init_field(self, value): 206 form = self.F(a=value) 207 form.validate() 208 return form.a 209 210 def test_pre_stop(self): 211 a = self._init_field("long") 212 self.assertEqual(a.errors, ["too long"]) 213 214 stoponly = self._init_field("stoponly") 215 self.assertEqual(stoponly.errors, []) 216 217 stopmessage = self._init_field("stopmessage") 218 self.assertEqual(stopmessage.errors, ["stop with message"]) 219 220 def test_post(self): 221 a = self._init_field("p") 222 self.assertEqual(a.errors, ["Post"]) 223 stopped = self._init_field("stop-post") 224 self.assertEqual(stopped.errors, ["stop with message", "Post-stopped"]) 225 226 227class SelectFieldTest(TestCase): 228 class F(Form): 229 a = SelectField(choices=[('a', 'hello'), ('btest', 'bye')], default='a') 230 b = SelectField(choices=[(1, 'Item 1'), (2, 'Item 2')], coerce=int, option_widget=widgets.TextInput()) 231 232 def test_defaults(self): 233 form = self.F() 234 self.assertEqual(form.a.data, 'a') 235 self.assertEqual(form.b.data, None) 236 self.assertEqual(form.validate(), False) 237 self.assertEqual(form.a(), """<select id="a" name="a"><option selected value="a">hello</option><option value="btest">bye</option></select>""") 238 self.assertEqual(form.b(), """<select id="b" name="b"><option value="1">Item 1</option><option value="2">Item 2</option></select>""") 239 240 def test_with_data(self): 241 form = self.F(DummyPostData(a=['btest'])) 242 self.assertEqual(form.a.data, 'btest') 243 self.assertEqual(form.a(), """<select id="a" name="a"><option value="a">hello</option><option selected value="btest">bye</option></select>""") 244 245 def test_value_coercion(self): 246 form = self.F(DummyPostData(b=['2'])) 247 self.assertEqual(form.b.data, 2) 248 self.assertTrue(form.b.validate(form)) 249 form = self.F(DummyPostData(b=['b'])) 250 self.assertEqual(form.b.data, None) 251 self.assertFalse(form.b.validate(form)) 252 253 def test_iterable_options(self): 254 form = self.F() 255 first_option = list(form.a)[0] 256 self.assertTrue(isinstance(first_option, form.a._Option)) 257 self.assertEqual( 258 list(text_type(x) for x in form.a), 259 ['<option selected value="a">hello</option>', '<option value="btest">bye</option>'] 260 ) 261 self.assertTrue(isinstance(first_option.widget, widgets.Option)) 262 self.assertTrue(isinstance(list(form.b)[0].widget, widgets.TextInput)) 263 self.assertEqual(first_option(disabled=True), '<option disabled selected value="a">hello</option>') 264 265 def test_default_coerce(self): 266 F = make_form(a=SelectField(choices=[('a', 'Foo')])) 267 form = F(DummyPostData(a=[])) 268 assert not form.validate() 269 self.assertEqual(form.a.data, 'None') 270 self.assertEqual(len(form.a.errors), 1) 271 self.assertEqual(form.a.errors[0], 'Not a valid choice') 272 273 274class SelectMultipleFieldTest(TestCase): 275 class F(Form): 276 a = SelectMultipleField(choices=[('a', 'hello'), ('b', 'bye'), ('c', 'something')], default=('a', )) 277 b = SelectMultipleField(coerce=int, choices=[(1, 'A'), (2, 'B'), (3, 'C')], default=("1", "3")) 278 279 def test_defaults(self): 280 form = self.F() 281 self.assertEqual(form.a.data, ['a']) 282 self.assertEqual(form.b.data, [1, 3]) 283 # Test for possible regression with null data 284 form.a.data = None 285 self.assertTrue(form.validate()) 286 self.assertEqual(list(form.a.iter_choices()), [(v, l, False) for v, l in form.a.choices]) 287 288 def test_with_data(self): 289 form = self.F(DummyPostData(a=['a', 'c'])) 290 self.assertEqual(form.a.data, ['a', 'c']) 291 self.assertEqual(list(form.a.iter_choices()), [('a', 'hello', True), ('b', 'bye', False), ('c', 'something', True)]) 292 self.assertEqual(form.b.data, []) 293 form = self.F(DummyPostData(b=['1', '2'])) 294 self.assertEqual(form.b.data, [1, 2]) 295 self.assertTrue(form.validate()) 296 form = self.F(DummyPostData(b=['1', '2', '4'])) 297 self.assertEqual(form.b.data, [1, 2, 4]) 298 self.assertFalse(form.validate()) 299 300 def test_coerce_fail(self): 301 form = self.F(b=['a']) 302 assert form.validate() 303 self.assertEqual(form.b.data, None) 304 form = self.F(DummyPostData(b=['fake'])) 305 assert not form.validate() 306 self.assertEqual(form.b.data, [1, 3]) 307 308 309class RadioFieldTest(TestCase): 310 class F(Form): 311 a = RadioField(choices=[('a', 'hello'), ('b', 'bye')], default='a') 312 b = RadioField(choices=[(1, 'Item 1'), (2, 'Item 2')], coerce=int) 313 314 def test(self): 315 form = self.F() 316 self.assertEqual(form.a.data, 'a') 317 self.assertEqual(form.b.data, None) 318 self.assertEqual(form.validate(), False) 319 self.assertEqual( 320 form.a(), 321 ( 322 """<ul id="a">""" 323 """<li><input checked id="a-0" name="a" type="radio" value="a"> <label for="a-0">hello</label></li>""" 324 """<li><input id="a-1" name="a" type="radio" value="b"> <label for="a-1">bye</label></li></ul>""" 325 ) 326 ) 327 self.assertEqual( 328 form.b(), 329 ( 330 """<ul id="b">""" 331 """<li><input id="b-0" name="b" type="radio" value="1"> <label for="b-0">Item 1</label></li>""" 332 """<li><input id="b-1" name="b" type="radio" value="2"> <label for="b-1">Item 2</label></li></ul>""" 333 ) 334 ) 335 self.assertEqual( 336 [text_type(x) for x in form.a], 337 ['<input checked id="a-0" name="a" type="radio" value="a">', '<input id="a-1" name="a" type="radio" value="b">'] 338 ) 339 340 def test_text_coercion(self): 341 # Regression test for text coercsion scenarios where the value is a boolean. 342 coerce_func = lambda x: False if x == 'False' else bool(x) 343 F = make_form(a=RadioField(choices=[(True, 'yes'), (False, 'no')], coerce=coerce_func)) 344 form = F() 345 self.assertEqual( 346 form.a(), 347 '''<ul id="a">''' 348 '''<li><input id="a-0" name="a" type="radio" value="True"> <label for="a-0">yes</label></li>''' 349 '''<li><input checked id="a-1" name="a" type="radio" value="False"> <label for="a-1">no</label></li></ul>''' 350 ) 351 352 353class TextFieldTest(TestCase): 354 class F(Form): 355 a = TextField() 356 357 def test(self): 358 form = self.F() 359 self.assertEqual(form.a.data, None) 360 self.assertEqual(form.a(), """<input id="a" name="a" type="text" value="">""") 361 form = self.F(DummyPostData(a=['hello'])) 362 self.assertEqual(form.a.data, 'hello') 363 self.assertEqual(form.a(), """<input id="a" name="a" type="text" value="hello">""") 364 form = self.F(DummyPostData(b=['hello'])) 365 self.assertEqual(form.a.data, '') 366 367 368class HiddenFieldTest(TestCase): 369 class F(Form): 370 a = HiddenField(default="LE DEFAULT") 371 372 def test(self): 373 form = self.F() 374 self.assertEqual(form.a(), """<input id="a" name="a" type="hidden" value="LE DEFAULT">""") 375 self.assertTrue(form.a.flags.hidden) 376 377 378class TextAreaFieldTest(TestCase): 379 class F(Form): 380 a = TextAreaField(default="LE DEFAULT") 381 382 def test(self): 383 form = self.F() 384 self.assertEqual(form.a(), """<textarea id="a" name="a">LE DEFAULT</textarea>""") 385 386 387class PasswordFieldTest(TestCase): 388 class F(Form): 389 a = PasswordField(widget=widgets.PasswordInput(hide_value=False), default="LE DEFAULT") 390 b = PasswordField(default="Hai") 391 392 def test(self): 393 form = self.F() 394 self.assertEqual(form.a(), """<input id="a" name="a" type="password" value="LE DEFAULT">""") 395 self.assertEqual(form.b(), """<input id="b" name="b" type="password" value="">""") 396 397 398class FileFieldTest(TestCase): 399 class F(Form): 400 a = FileField(default="LE DEFAULT") 401 402 def test(self): 403 form = self.F() 404 self.assertEqual(form.a(), """<input id="a" name="a" type="file">""") 405 406 407class IntegerFieldTest(TestCase): 408 class F(Form): 409 a = IntegerField() 410 b = IntegerField(default=48) 411 412 def test(self): 413 form = self.F(DummyPostData(a=['v'], b=['-15'])) 414 self.assertEqual(form.a.data, None) 415 self.assertEqual(form.a.raw_data, ['v']) 416 self.assertEqual(form.a(), """<input id="a" name="a" type="text" value="v">""") 417 self.assertEqual(form.b.data, -15) 418 self.assertEqual(form.b(), """<input id="b" name="b" type="text" value="-15">""") 419 self.assertTrue(not form.a.validate(form)) 420 self.assertTrue(form.b.validate(form)) 421 form = self.F(DummyPostData(a=[], b=[''])) 422 self.assertEqual(form.a.data, None) 423 self.assertEqual(form.a.raw_data, []) 424 self.assertEqual(form.b.data, None) 425 self.assertEqual(form.b.raw_data, ['']) 426 self.assertTrue(not form.validate()) 427 self.assertEqual(len(form.b.process_errors), 1) 428 self.assertEqual(len(form.b.errors), 1) 429 form = self.F(b=9) 430 self.assertEqual(form.b.data, 9) 431 self.assertEqual(form.a._value(), '') 432 self.assertEqual(form.b._value(), '9') 433 434 435class DecimalFieldTest(TestCase): 436 def test(self): 437 F = make_form(a=DecimalField()) 438 form = F(DummyPostData(a='2.1')) 439 self.assertEqual(form.a.data, Decimal('2.1')) 440 self.assertEqual(form.a._value(), '2.1') 441 form.a.raw_data = None 442 self.assertEqual(form.a._value(), '2.10') 443 self.assertTrue(form.validate()) 444 form = F(DummyPostData(a='2,1'), a=Decimal(5)) 445 self.assertEqual(form.a.data, None) 446 self.assertEqual(form.a.raw_data, ['2,1']) 447 self.assertFalse(form.validate()) 448 form = F(DummyPostData(a='asdf'), a=Decimal('.21')) 449 self.assertEqual(form.a._value(), 'asdf') 450 assert not form.validate() 451 452 def test_quantize(self): 453 F = make_form(a=DecimalField(places=3, rounding=ROUND_UP), b=DecimalField(places=None)) 454 form = F(a=Decimal('3.1415926535')) 455 self.assertEqual(form.a._value(), '3.142') 456 form.a.rounding = ROUND_DOWN 457 self.assertEqual(form.a._value(), '3.141') 458 self.assertEqual(form.b._value(), '') 459 form = F(a=3.14159265, b=72) 460 self.assertEqual(form.a._value(), '3.142') 461 self.assertTrue(isinstance(form.a.data, float)) 462 self.assertEqual(form.b._value(), '72') 463 464 465class FloatFieldTest(TestCase): 466 class F(Form): 467 a = FloatField() 468 b = FloatField(default=48.0) 469 470 def test(self): 471 form = self.F(DummyPostData(a=['v'], b=['-15.0'])) 472 self.assertEqual(form.a.data, None) 473 self.assertEqual(form.a.raw_data, ['v']) 474 self.assertEqual(form.a(), """<input id="a" name="a" type="text" value="v">""") 475 self.assertEqual(form.b.data, -15.0) 476 self.assertEqual(form.b(), """<input id="b" name="b" type="text" value="-15.0">""") 477 self.assertFalse(form.a.validate(form)) 478 self.assertTrue(form.b.validate(form)) 479 form = self.F(DummyPostData(a=[], b=[''])) 480 self.assertEqual(form.a.data, None) 481 self.assertEqual(form.a._value(), '') 482 self.assertEqual(form.b.data, None) 483 self.assertEqual(form.b.raw_data, ['']) 484 self.assertFalse(form.validate()) 485 self.assertEqual(len(form.b.process_errors), 1) 486 self.assertEqual(len(form.b.errors), 1) 487 form = self.F(b=9.0) 488 self.assertEqual(form.b.data, 9.0) 489 self.assertEqual(form.b._value(), "9.0") 490 491 492class BooleanFieldTest(TestCase): 493 class BoringForm(Form): 494 bool1 = BooleanField() 495 bool2 = BooleanField(default=True, false_values=()) 496 497 obj = AttrDict(bool1=None, bool2=True) 498 499 def test_defaults(self): 500 # Test with no post data to make sure defaults work 501 form = self.BoringForm() 502 self.assertEqual(form.bool1.raw_data, None) 503 self.assertEqual(form.bool1.data, False) 504 self.assertEqual(form.bool2.data, True) 505 506 def test_rendering(self): 507 form = self.BoringForm(DummyPostData(bool2="x")) 508 self.assertEqual(form.bool1(), '<input id="bool1" name="bool1" type="checkbox" value="y">') 509 self.assertEqual(form.bool2(), '<input checked id="bool2" name="bool2" type="checkbox" value="x">') 510 self.assertEqual(form.bool2.raw_data, ['x']) 511 512 def test_with_postdata(self): 513 form = self.BoringForm(DummyPostData(bool1=['a'])) 514 self.assertEqual(form.bool1.raw_data, ['a']) 515 self.assertEqual(form.bool1.data, True) 516 form = self.BoringForm(DummyPostData(bool1=['false'], bool2=['false'])) 517 self.assertEqual(form.bool1.data, False) 518 self.assertEqual(form.bool2.data, True) 519 520 def test_with_model_data(self): 521 form = self.BoringForm(obj=self.obj) 522 self.assertEqual(form.bool1.data, False) 523 self.assertEqual(form.bool1.raw_data, None) 524 self.assertEqual(form.bool2.data, True) 525 526 def test_with_postdata_and_model(self): 527 form = self.BoringForm(DummyPostData(bool1=['y']), obj=self.obj) 528 self.assertEqual(form.bool1.data, True) 529 self.assertEqual(form.bool2.data, False) 530 531 532class DateFieldTest(TestCase): 533 class F(Form): 534 a = DateField() 535 b = DateField(format='%m/%d %Y') 536 537 def test_basic(self): 538 d = date(2008, 5, 7) 539 form = self.F(DummyPostData(a=['2008-05-07'], b=['05/07', '2008'])) 540 self.assertEqual(form.a.data, d) 541 self.assertEqual(form.a._value(), '2008-05-07') 542 self.assertEqual(form.b.data, d) 543 self.assertEqual(form.b._value(), '05/07 2008') 544 545 def test_failure(self): 546 form = self.F(DummyPostData(a=['2008-bb-cc'], b=['hi'])) 547 assert not form.validate() 548 self.assertEqual(len(form.a.process_errors), 1) 549 self.assertEqual(len(form.a.errors), 1) 550 self.assertEqual(len(form.b.errors), 1) 551 self.assertEqual(form.a.process_errors[0], 'Not a valid date value') 552 553 554class DateTimeFieldTest(TestCase): 555 class F(Form): 556 a = DateTimeField() 557 b = DateTimeField(format='%Y-%m-%d %H:%M') 558 559 def test_basic(self): 560 d = datetime(2008, 5, 5, 4, 30, 0, 0) 561 # Basic test with both inputs 562 form = self.F(DummyPostData(a=['2008-05-05', '04:30:00'], b=['2008-05-05 04:30'])) 563 self.assertEqual(form.a.data, d) 564 self.assertEqual(form.a(), """<input id="a" name="a" type="text" value="2008-05-05 04:30:00">""") 565 self.assertEqual(form.b.data, d) 566 self.assertEqual(form.b(), """<input id="b" name="b" type="text" value="2008-05-05 04:30">""") 567 self.assertTrue(form.validate()) 568 569 # Test with a missing input 570 form = self.F(DummyPostData(a=['2008-05-05'])) 571 self.assertFalse(form.validate()) 572 self.assertEqual(form.a.errors[0], 'Not a valid datetime value') 573 574 form = self.F(a=d, b=d) 575 self.assertTrue(form.validate()) 576 self.assertEqual(form.a._value(), '2008-05-05 04:30:00') 577 578 def test_microseconds(self): 579 d = datetime(2011, 5, 7, 3, 23, 14, 424200) 580 F = make_form(a=DateTimeField(format='%Y-%m-%d %H:%M:%S.%f')) 581 form = F(DummyPostData(a=['2011-05-07 03:23:14.4242'])) 582 self.assertEqual(d, form.a.data) 583 584 585class SubmitFieldTest(TestCase): 586 class F(Form): 587 a = SubmitField('Label') 588 589 def test(self): 590 self.assertEqual(self.F().a(), """<input id="a" name="a" type="submit" value="Label">""") 591 592 593class FormFieldTest(TestCase): 594 def setUp(self): 595 F = make_form( 596 a=TextField(validators=[validators.required()]), 597 b=TextField(), 598 ) 599 self.F1 = make_form('F1', a=FormField(F)) 600 self.F2 = make_form('F2', a=FormField(F, separator='::')) 601 602 def test_formdata(self): 603 form = self.F1(DummyPostData({'a-a': ['moo']})) 604 self.assertEqual(form.a.form.a.name, 'a-a') 605 self.assertEqual(form.a['a'].data, 'moo') 606 self.assertEqual(form.a['b'].data, '') 607 self.assertTrue(form.validate()) 608 609 def test_iteration(self): 610 self.assertEqual([x.name for x in self.F1().a], ['a-a', 'a-b']) 611 612 def test_with_obj(self): 613 obj = AttrDict(a=AttrDict(a='mmm')) 614 form = self.F1(obj=obj) 615 self.assertEqual(form.a.form.a.data, 'mmm') 616 self.assertEqual(form.a.form.b.data, None) 617 obj_inner = AttrDict(a=None, b='rawr') 618 obj2 = AttrDict(a=obj_inner) 619 form.populate_obj(obj2) 620 self.assertTrue(obj2.a is obj_inner) 621 self.assertEqual(obj_inner.a, 'mmm') 622 self.assertEqual(obj_inner.b, None) 623 624 def test_widget(self): 625 self.assertEqual( 626 self.F1().a(), 627 '''<table id="a">''' 628 '''<tr><th><label for="a-a">A</label></th><td><input id="a-a" name="a-a" type="text" value=""></td></tr>''' 629 '''<tr><th><label for="a-b">B</label></th><td><input id="a-b" name="a-b" type="text" value=""></td></tr>''' 630 '''</table>''' 631 ) 632 633 def test_separator(self): 634 form = self.F2(DummyPostData({'a-a': 'fake', 'a::a': 'real'})) 635 self.assertEqual(form.a.a.name, 'a::a') 636 self.assertEqual(form.a.a.data, 'real') 637 self.assertTrue(form.validate()) 638 639 def test_no_validators_or_filters(self): 640 class A(Form): 641 a = FormField(self.F1, validators=[validators.required()]) 642 self.assertRaises(TypeError, A) 643 644 class B(Form): 645 a = FormField(self.F1, filters=[lambda x: x]) 646 self.assertRaises(TypeError, B) 647 648 class C(Form): 649 a = FormField(self.F1) 650 651 def validate_a(form, field): 652 pass 653 form = C() 654 self.assertRaises(TypeError, form.validate) 655 656 def test_populate_missing_obj(self): 657 obj = AttrDict(a=None) 658 obj2 = AttrDict(a=AttrDict(a='mmm')) 659 form = self.F1() 660 self.assertRaises(TypeError, form.populate_obj, obj) 661 form.populate_obj(obj2) 662 663 664class FieldListTest(TestCase): 665 t = TextField(validators=[validators.Required()]) 666 667 def test_form(self): 668 F = make_form(a=FieldList(self.t)) 669 data = ['foo', 'hi', 'rawr'] 670 a = F(a=data).a 671 self.assertEqual(a.entries[1].data, 'hi') 672 self.assertEqual(a.entries[1].name, 'a-1') 673 self.assertEqual(a.data, data) 674 self.assertEqual(len(a.entries), 3) 675 676 pdata = DummyPostData({'a-0': ['bleh'], 'a-3': ['yarg'], 'a-4': [''], 'a-7': ['mmm']}) 677 form = F(pdata) 678 self.assertEqual(len(form.a.entries), 4) 679 self.assertEqual(form.a.data, ['bleh', 'yarg', '', 'mmm']) 680 self.assertFalse(form.validate()) 681 682 form = F(pdata, a=data) 683 self.assertEqual(form.a.data, ['bleh', 'yarg', '', 'mmm']) 684 self.assertFalse(form.validate()) 685 686 # Test for formdata precedence 687 pdata = DummyPostData({'a-0': ['a'], 'a-1': ['b']}) 688 form = F(pdata, a=data) 689 self.assertEqual(len(form.a.entries), 2) 690 self.assertEqual(form.a.data, ['a', 'b']) 691 self.assertEqual(list(iter(form.a)), list(form.a.entries)) 692 693 def test_enclosed_subform(self): 694 make_inner = lambda: AttrDict(a=None) 695 F = make_form( 696 a=FieldList(FormField(make_form('FChild', a=self.t), default=make_inner)) 697 ) 698 data = [{'a': 'hello'}] 699 form = F(a=data) 700 self.assertEqual(form.a.data, data) 701 self.assertTrue(form.validate()) 702 form.a.append_entry() 703 self.assertEqual(form.a.data, data + [{'a': None}]) 704 self.assertFalse(form.validate()) 705 706 pdata = DummyPostData({'a-0': ['fake'], 'a-0-a': ['foo'], 'a-1-a': ['bar']}) 707 form = F(pdata, a=data) 708 self.assertEqual(form.a.data, [{'a': 'foo'}, {'a': 'bar'}]) 709 710 inner_obj = make_inner() 711 inner_list = [inner_obj] 712 obj = AttrDict(a=inner_list) 713 form.populate_obj(obj) 714 self.assertTrue(obj.a is not inner_list) 715 self.assertEqual(len(obj.a), 2) 716 self.assertTrue(obj.a[0] is inner_obj) 717 self.assertEqual(obj.a[0].a, 'foo') 718 self.assertEqual(obj.a[1].a, 'bar') 719 720 # Test failure on populate 721 obj2 = AttrDict(a=42) 722 self.assertRaises(TypeError, form.populate_obj, obj2) 723 724 def test_entry_management(self): 725 F = make_form(a=FieldList(self.t)) 726 a = F(a=['hello', 'bye']).a 727 self.assertEqual(a.pop_entry().name, 'a-1') 728 self.assertEqual(a.data, ['hello']) 729 a.append_entry('orange') 730 self.assertEqual(a.data, ['hello', 'orange']) 731 self.assertEqual(a[-1].name, 'a-1') 732 self.assertEqual(a.pop_entry().data, 'orange') 733 self.assertEqual(a.pop_entry().name, 'a-0') 734 self.assertRaises(IndexError, a.pop_entry) 735 736 def test_min_max_entries(self): 737 F = make_form(a=FieldList(self.t, min_entries=1, max_entries=3)) 738 a = F().a 739 self.assertEqual(len(a), 1) 740 self.assertEqual(a[0].data, None) 741 big_input = ['foo', 'flaf', 'bar', 'baz'] 742 self.assertRaises(AssertionError, F, a=big_input) 743 pdata = DummyPostData(('a-%d' % i, v) for i, v in enumerate(big_input)) 744 a = F(pdata).a 745 self.assertEqual(a.data, ['foo', 'flaf', 'bar']) 746 self.assertRaises(AssertionError, a.append_entry) 747 748 def test_validators(self): 749 def validator(form, field): 750 if field.data and field.data[0] == 'fail': 751 raise ValueError('fail') 752 elif len(field.data) > 2: 753 raise ValueError('too many') 754 755 F = make_form(a=FieldList(self.t, validators=[validator])) 756 757 # Case 1: length checking validators work as expected. 758 fdata = DummyPostData({'a-0': ['hello'], 'a-1': ['bye'], 'a-2': ['test3']}) 759 form = F(fdata) 760 assert not form.validate() 761 self.assertEqual(form.a.errors, ['too many']) 762 763 # Case 2: checking a value within. 764 fdata['a-0'] = ['fail'] 765 form = F(fdata) 766 assert not form.validate() 767 self.assertEqual(form.a.errors, ['fail']) 768 769 # Case 3: normal field validator still works 770 form = F(DummyPostData({'a-0': ['']})) 771 assert not form.validate() 772 self.assertEqual(form.a.errors, [['This field is required.']]) 773 774 def test_no_filters(self): 775 my_filter = lambda x: x 776 self.assertRaises(TypeError, FieldList, self.t, filters=[my_filter], _form=Form(), _name='foo') 777 778 def test_process_prefilled(self): 779 data = ['foo', 'hi', 'rawr'] 780 781 class A(object): 782 def __init__(self, a): 783 self.a = a 784 obj = A(data) 785 F = make_form(a=FieldList(self.t)) 786 # fill form 787 form = F(obj=obj) 788 self.assertEqual(len(form.a.entries), 3) 789 # pretend to submit form unchanged 790 pdata = DummyPostData({ 791 'a-0': ['foo'], 792 'a-1': ['hi'], 793 'a-2': ['rawr']}) 794 form.process(formdata=pdata) 795 # check if data still the same 796 self.assertEqual(len(form.a.entries), 3) 797 self.assertEqual(form.a.data, data) 798 799 800class MyCustomField(TextField): 801 def process_data(self, data): 802 if data == 'fail': 803 raise ValueError('Contrived Failure') 804 805 return super(MyCustomField, self).process_data(data) 806 807 808class CustomFieldQuirksTest(TestCase): 809 class F(Form): 810 a = MyCustomField() 811 b = SelectFieldBase() 812 813 def test_processing_failure(self): 814 form = self.F(a='42') 815 assert form.validate() 816 form = self.F(a='fail') 817 assert not form.validate() 818 819 def test_default_impls(self): 820 f = self.F() 821 self.assertRaises(NotImplementedError, f.b.iter_choices) 822 823 824class HTML5FieldsTest(TestCase): 825 class F(Form): 826 search = html5.SearchField() 827 telephone = html5.TelField() 828 url = html5.URLField() 829 email = html5.EmailField() 830 datetime = html5.DateTimeField() 831 date = html5.DateField() 832 dt_local = html5.DateTimeLocalField() 833 integer = html5.IntegerField() 834 decimal = html5.DecimalField() 835 int_range = html5.IntegerRangeField() 836 decimal_range = html5.DecimalRangeField() 837 838 def _build_value(self, key, form_input, expected_html, data=unset_value): 839 if data is unset_value: 840 data = form_input 841 if expected_html.startswith('type='): 842 expected_html = '<input id="%s" name="%s" %s value="%s">' % (key, key, expected_html, form_input) 843 return { 844 'key': key, 845 'form_input': form_input, 846 'expected_html': expected_html, 847 'data': data 848 } 849 850 def test_simple(self): 851 b = self._build_value 852 VALUES = ( 853 b('search', 'search', 'type="search"'), 854 b('telephone', '123456789', 'type="tel"'), 855 b('url', 'http://wtforms.simplecodes.com/', 'type="url"'), 856 b('email', 'foo@bar.com', 'type="email"'), 857 b('datetime', '2013-09-05 00:23:42', 'type="datetime"', datetime(2013, 9, 5, 0, 23, 42)), 858 b('date', '2013-09-05', 'type="date"', date(2013, 9, 5)), 859 b('dt_local', '2013-09-05 00:23:42', 'type="datetime-local"', datetime(2013, 9, 5, 0, 23, 42)), 860 b('integer', '42', '<input id="integer" name="integer" step="1" type="number" value="42">', 42), 861 b('decimal', '43.5', '<input id="decimal" name="decimal" step="any" type="number" value="43.5">', Decimal('43.5')), 862 b('int_range', '4', '<input id="int_range" name="int_range" step="1" type="range" value="4">', 4), 863 b('decimal_range', '58', '<input id="decimal_range" name="decimal_range" step="any" type="range" value="58">', 58), 864 ) 865 formdata = DummyPostData() 866 kw = {} 867 for item in VALUES: 868 formdata[item['key']] = item['form_input'] 869 kw[item['key']] = item['data'] 870 871 form = self.F(formdata) 872 for item in VALUES: 873 field = form[item['key']] 874 render_value = field() 875 if render_value != item['expected_html']: 876 tmpl = 'Field {key} render mismatch: {render_value!r} != {expected_html!r}' 877 raise AssertionError(tmpl.format(render_value=render_value, **item)) 878 if field.data != item['data']: 879 tmpl = 'Field {key} data mismatch: {field.data!r} != {data!r}' 880 raise AssertionError(tmpl.format(field=field, **item)) 881