/deform/tests/test_functional.py

https://github.com/encukou/deform · Python · 255 lines · 229 code · 26 blank · 0 comment · 11 complexity · 8b9ececac39a279f26bd5c0c3af5e82d MD5 · raw file

  1. import unittest
  2. import datetime
  3. import colander
  4. import deform.widget
  5. class TestFunctional(unittest.TestCase):
  6. def _makeSchema(self):
  7. from colander import MappingSchema
  8. from colander import SequenceSchema
  9. from colander import SchemaNode
  10. from colander import String
  11. from colander import Boolean
  12. from colander import Date
  13. class DatesSchema(SequenceSchema):
  14. date = SchemaNode(Date())
  15. class SeriesSchema(MappingSchema):
  16. name = SchemaNode(String())
  17. dates = DatesSchema()
  18. class MySchema(MappingSchema):
  19. name = SchemaNode(String())
  20. title = SchemaNode(String())
  21. cool = SchemaNode(Boolean(), default=True, missing=True)
  22. series = SeriesSchema()
  23. schema = MySchema()
  24. return schema
  25. def _makeForm(self, schema):
  26. from deform.form import Form
  27. return Form(schema, formid='myform')
  28. def _soupify(self, html):
  29. from BeautifulSoup import BeautifulSoup
  30. return BeautifulSoup(html)
  31. def test_render_empty(self):
  32. schema = self._makeSchema()
  33. form = self._makeForm(schema)
  34. html = form.render()
  35. soup = self._soupify(html)
  36. form = soup.form
  37. self.assertEqual(form['action'], '')
  38. inputs = form.findAll('input')
  39. self.assertEqual(inputs[0]['name'], '_charset_')
  40. self.assertEqual(inputs[1]['name'], '__formid__')
  41. self.assertEqual(inputs[1]['value'], 'myform')
  42. self.assertEqual(inputs[2]['name'], 'name')
  43. self.assertEqual(inputs[2]['value'], '')
  44. self.assertEqual(inputs[3]['name'], 'title')
  45. self.assertEqual(inputs[3]['value'], '')
  46. self.assertEqual(inputs[4]['name'], 'cool')
  47. self.assertEqual(inputs[4].get('checked'), 'True')
  48. self.assertEqual(inputs[5]['name'], '__start__')
  49. self.assertEqual(inputs[5]['value'], 'series:mapping')
  50. self.assertEqual(inputs[6]['name'], 'name')
  51. self.assertEqual(inputs[6]['value'], '')
  52. self.assertEqual(inputs[7]['name'], '__start__')
  53. self.assertEqual(inputs[7]['value'], 'dates:sequence')
  54. self.assertEqual(inputs[8]['name'], '__end__')
  55. self.assertEqual(inputs[8]['value'], 'dates:sequence')
  56. self.assertEqual(inputs[9]['name'], '__end__')
  57. self.assertEqual(inputs[9]['value'], 'series:mapping')
  58. def test_render_not_empty(self):
  59. schema = self._makeSchema()
  60. form = self._makeForm(schema)
  61. appstruct = {
  62. 'cool':False,
  63. 'series':{
  64. 'dates':[datetime.date(2010, 3, 21)],
  65. }
  66. }
  67. html = form.render(appstruct)
  68. soup = self._soupify(html)
  69. form = soup.form
  70. self.assertEqual(form['action'], '')
  71. inputs = form.findAll('input')
  72. self.assertEqual(inputs[0]['name'], '_charset_')
  73. self.assertEqual(inputs[1]['name'], '__formid__')
  74. self.assertEqual(inputs[1]['value'], 'myform')
  75. self.assertEqual(inputs[2]['name'], 'name')
  76. self.assertEqual(inputs[2]['value'], '')
  77. self.assertEqual(inputs[3]['name'], 'title')
  78. self.assertEqual(inputs[3]['value'], '')
  79. self.assertEqual(inputs[4]['name'], 'cool')
  80. self.assertEqual(inputs[4].get('checked'), None)
  81. self.assertEqual(inputs[5]['name'], '__start__')
  82. self.assertEqual(inputs[5]['value'], 'series:mapping')
  83. self.assertEqual(inputs[6]['name'], 'name')
  84. self.assertEqual(inputs[6]['value'], '')
  85. self.assertEqual(inputs[7]['name'], '__start__')
  86. self.assertEqual(inputs[7]['value'], 'dates:sequence')
  87. self.assertEqual(inputs[8]['name'], 'date')
  88. self.assertEqual(inputs[8]['value'], '2010-03-21')
  89. self.assertEqual(inputs[9]['name'], '__end__')
  90. self.assertEqual(inputs[9]['value'], 'dates:sequence')
  91. self.assertEqual(inputs[10]['name'], '__end__')
  92. self.assertEqual(inputs[10]['value'], 'series:mapping')
  93. def test_widget_deserialize(self):
  94. filled = {
  95. 'name': 'project1',
  96. 'title': 'Cool project',
  97. 'series': {
  98. 'name':'date series 1',
  99. 'dates': ['2008-10-12', '2009-10-12'],
  100. }
  101. }
  102. schema = self._makeSchema()
  103. form = self._makeForm(schema)
  104. result = form.widget.deserialize(form, filled)
  105. expected = {'series':
  106. {'dates': ['2008-10-12', '2009-10-12'],
  107. 'name': 'date series 1'},
  108. 'cool': 'false',
  109. 'name': 'project1',
  110. 'title': 'Cool project'}
  111. self.assertEqual(result, expected)
  112. def test_validate(self):
  113. from colander import null
  114. from deform.exception import ValidationFailure
  115. schema = self._makeSchema()
  116. form = self._makeForm(schema)
  117. try:
  118. form.validate([])
  119. except ValidationFailure, ve:
  120. e = ve.error
  121. self.assertEqual(form.error, e)
  122. self.assertEqual(form.children[0].error, e.children[0])
  123. self.assertEqual(form.children[1].error, e.children[1])
  124. self.assertEqual(form.children[3].error, e.children[2])
  125. self.assertEqual(form.children[3].children[0].error,
  126. e.children[2].children[0])
  127. self.assertEqual(
  128. ve.cstruct,
  129. {
  130. 'series': {'dates': [], 'name': null},
  131. 'cool': 'false',
  132. 'name': null,
  133. 'title': null,
  134. }
  135. )
  136. @colander.deferred
  137. def deferred_date_validator(node, kw):
  138. max_date = kw.get('max_date')
  139. if max_date is None: max_date = datetime.date.today()
  140. return colander.Range(min=datetime.date.min, max=max_date)
  141. @colander.deferred
  142. def deferred_date_description(node, kw):
  143. max_date = kw.get('max_date')
  144. if max_date is None: max_date = datetime.date.today()
  145. return 'Blog post date (no earlier than %s)' % max_date.ctime()
  146. @colander.deferred
  147. def deferred_date_missing(node, kw):
  148. default_date = kw.get('default_date')
  149. if default_date is None: default_date = datetime.date.today()
  150. return default_date
  151. @colander.deferred
  152. def deferred_body_validator(node, kw):
  153. max_bodylen = kw.get('max_bodylen')
  154. if max_bodylen is None: max_bodylen = 1 << 18
  155. return colander.Length(max=max_bodylen)
  156. @colander.deferred
  157. def deferred_body_description(node, kw):
  158. max_bodylen = kw.get('max_bodylen')
  159. if max_bodylen is None: max_bodylen = 1 << 18
  160. return 'Blog post body (no longer than %s bytes)' % max_bodylen
  161. @colander.deferred
  162. def deferred_body_widget(node, kw):
  163. body_type = kw.get('body_type')
  164. if body_type == 'richtext':
  165. widget = deform.widget.RichTextWidget()
  166. else: # pragma: no cover
  167. widget = deform.widget.TextAreaWidget()
  168. return widget
  169. @colander.deferred
  170. def deferred_category_validator(node, kw):
  171. categories = kw.get('categories', [])
  172. return colander.OneOf([ x[0] for x in categories ])
  173. @colander.deferred
  174. def deferred_category_widget(node, kw):
  175. categories = kw.get('categories', [])
  176. return deform.widget.RadioChoiceWidget(values=categories)
  177. class BlogPostSchema(colander.Schema):
  178. title = colander.SchemaNode(
  179. colander.String(),
  180. title = 'Title',
  181. description = 'Blog post title',
  182. validator = colander.Length(min=5, max=100),
  183. widget = deform.widget.TextInputWidget(),
  184. )
  185. date = colander.SchemaNode(
  186. colander.Date(),
  187. title = 'Date',
  188. missing = deferred_date_missing,
  189. description = deferred_date_description,
  190. validator = deferred_date_validator,
  191. widget = deform.widget.DateInputWidget(),
  192. )
  193. body = colander.SchemaNode(
  194. colander.String(),
  195. title = 'Body',
  196. description = deferred_body_description,
  197. validator = deferred_body_validator,
  198. widget = deferred_body_widget,
  199. )
  200. category = colander.SchemaNode(
  201. colander.String(),
  202. title = 'Category',
  203. description = 'Blog post category',
  204. validator = deferred_category_validator,
  205. widget = deferred_category_widget,
  206. )
  207. def remove_date(node, kw):
  208. if kw.get('nodates'): del node['date']
  209. class TestDeferredFunction(unittest.TestCase):
  210. def test_it(self):
  211. schema = BlogPostSchema(after_bind=remove_date).bind(
  212. max_date = datetime.date.max,
  213. max_bodylen = 5000,
  214. body_type = 'richtext',
  215. default_date = datetime.date.today(),
  216. categories = [('one', 'One'), ('two', 'Two')]
  217. )
  218. self.assertEqual(schema['date'].missing, datetime.date.today())
  219. self.assertEqual(schema['date'].validator.max, datetime.date.max)
  220. self.assertEqual(schema['date'].widget.__class__.__name__,
  221. 'DateInputWidget')
  222. self.assertEqual(schema['body'].description,
  223. 'Blog post body (no longer than 5000 bytes)')
  224. self.assertEqual(schema['body'].validator.max, 5000)
  225. self.assertEqual(schema['body'].widget.__class__.__name__,
  226. 'RichTextWidget')
  227. self.assertEqual(schema['category'].validator.choices, ['one', 'two'])
  228. self.assertEqual(schema['category'].widget.values,
  229. [('one', 'One'), ('two', 'Two')])