PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/django/contrib/formtools/tests/__init__.py

https://github.com/Tippr/django
Python | 462 lines | 378 code | 21 blank | 63 comment | 4 complexity | 107a26c9f1276493ef80ef3146557f13 MD5 | raw file
  1. import os
  2. import re
  3. import warnings
  4. from django import http
  5. from django.conf import settings
  6. from django.contrib.formtools import preview, utils
  7. from django.contrib.formtools.wizard import FormWizard
  8. from django.test import TestCase
  9. from django.test.utils import get_warnings_state, restore_warnings_state
  10. from django.utils import unittest
  11. from django.contrib.formtools.tests.wizard import *
  12. from django.contrib.formtools.tests.forms import *
  13. warnings.filterwarnings('ignore', category=PendingDeprecationWarning,
  14. module='django.contrib.formtools.wizard')
  15. success_string = "Done was called!"
  16. class TestFormPreview(preview.FormPreview):
  17. def get_context(self, request, form):
  18. context = super(TestFormPreview, self).get_context(request, form)
  19. context.update({'custom_context': True})
  20. return context
  21. def get_initial(self, request):
  22. return {'field1': 'Works!'}
  23. def done(self, request, cleaned_data):
  24. return http.HttpResponse(success_string)
  25. class FormToolsTestCase(TestCase):
  26. def setUp(self):
  27. # in the test runner use templates/tests/ to provide base.html
  28. self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
  29. settings.TEMPLATE_DIRS = list(settings.TEMPLATE_DIRS) + [
  30. os.path.join(os.path.dirname(__file__), 'templates')]
  31. def tearDown(self):
  32. settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
  33. class PreviewTests(FormToolsTestCase):
  34. urls = 'django.contrib.formtools.tests.urls'
  35. def setUp(self):
  36. super(PreviewTests, self).setUp()
  37. self.save_warnings_state()
  38. warnings.filterwarnings('ignore', category=DeprecationWarning,
  39. module='django.contrib.formtools.utils')
  40. # Create a FormPreview instance to share between tests
  41. self.preview = preview.FormPreview(TestForm)
  42. input_template = '<input type="hidden" name="%s" value="%s" />'
  43. self.input = input_template % (self.preview.unused_name('stage'), "%d")
  44. self.test_data = {'field1':u'foo', 'field1_':u'asdf'}
  45. def tearDown(self):
  46. super(PreviewTests, self).tearDown()
  47. self.restore_warnings_state()
  48. def test_unused_name(self):
  49. """
  50. Verifies name mangling to get uniue field name.
  51. """
  52. self.assertEqual(self.preview.unused_name('field1'), 'field1__')
  53. def test_form_get(self):
  54. """
  55. Test contrib.formtools.preview form retrieval.
  56. Use the client library to see if we can sucessfully retrieve
  57. the form (mostly testing the setup ROOT_URLCONF
  58. process). Verify that an additional hidden input field
  59. is created to manage the stage.
  60. """
  61. response = self.client.get('/preview/')
  62. stage = self.input % 1
  63. self.assertContains(response, stage, 1)
  64. self.assertEqual(response.context['custom_context'], True)
  65. self.assertEqual(response.context['form'].initial, {'field1': 'Works!'})
  66. def test_form_preview(self):
  67. """
  68. Test contrib.formtools.preview form preview rendering.
  69. Use the client library to POST to the form to see if a preview
  70. is returned. If we do get a form back check that the hidden
  71. value is correctly managing the state of the form.
  72. """
  73. # Pass strings for form submittal and add stage variable to
  74. # show we previously saw first stage of the form.
  75. self.test_data.update({'stage': 1})
  76. response = self.client.post('/preview/', self.test_data)
  77. # Check to confirm stage is set to 2 in output form.
  78. stage = self.input % 2
  79. self.assertContains(response, stage, 1)
  80. def test_form_submit(self):
  81. """
  82. Test contrib.formtools.preview form submittal.
  83. Use the client library to POST to the form with stage set to 3
  84. to see if our forms done() method is called. Check first
  85. without the security hash, verify failure, retry with security
  86. hash and verify sucess.
  87. """
  88. # Pass strings for form submittal and add stage variable to
  89. # show we previously saw first stage of the form.
  90. self.test_data.update({'stage':2})
  91. response = self.client.post('/preview/', self.test_data)
  92. self.assertNotEqual(response.content, success_string)
  93. hash = self.preview.security_hash(None, TestForm(self.test_data))
  94. self.test_data.update({'hash': hash})
  95. response = self.client.post('/preview/', self.test_data)
  96. self.assertEqual(response.content, success_string)
  97. def test_bool_submit(self):
  98. """
  99. Test contrib.formtools.preview form submittal when form contains:
  100. BooleanField(required=False)
  101. Ticket: #6209 - When an unchecked BooleanField is previewed, the preview
  102. form's hash would be computed with no value for ``bool1``. However, when
  103. the preview form is rendered, the unchecked hidden BooleanField would be
  104. rendered with the string value 'False'. So when the preview form is
  105. resubmitted, the hash would be computed with the value 'False' for
  106. ``bool1``. We need to make sure the hashes are the same in both cases.
  107. """
  108. self.test_data.update({'stage':2})
  109. hash = self.preview.security_hash(None, TestForm(self.test_data))
  110. self.test_data.update({'hash':hash, 'bool1':u'False'})
  111. response = self.client.post('/preview/', self.test_data)
  112. self.assertEqual(response.content, success_string)
  113. def test_form_submit_good_hash(self):
  114. """
  115. Test contrib.formtools.preview form submittal, using a correct
  116. hash
  117. """
  118. # Pass strings for form submittal and add stage variable to
  119. # show we previously saw first stage of the form.
  120. self.test_data.update({'stage':2})
  121. response = self.client.post('/preview/', self.test_data)
  122. self.assertNotEqual(response.content, success_string)
  123. hash = utils.form_hmac(TestForm(self.test_data))
  124. self.test_data.update({'hash': hash})
  125. response = self.client.post('/preview/', self.test_data)
  126. self.assertEqual(response.content, success_string)
  127. def test_form_submit_bad_hash(self):
  128. """
  129. Test contrib.formtools.preview form submittal does not proceed
  130. if the hash is incorrect.
  131. """
  132. # Pass strings for form submittal and add stage variable to
  133. # show we previously saw first stage of the form.
  134. self.test_data.update({'stage':2})
  135. response = self.client.post('/preview/', self.test_data)
  136. self.assertEqual(response.status_code, 200)
  137. self.assertNotEqual(response.content, success_string)
  138. hash = utils.form_hmac(TestForm(self.test_data)) + "bad"
  139. self.test_data.update({'hash': hash})
  140. response = self.client.post('/previewpreview/', self.test_data)
  141. self.assertNotEqual(response.content, success_string)
  142. class SecurityHashTests(unittest.TestCase):
  143. def setUp(self):
  144. self._warnings_state = get_warnings_state()
  145. warnings.filterwarnings('ignore', category=DeprecationWarning,
  146. module='django.contrib.formtools.utils')
  147. def tearDown(self):
  148. restore_warnings_state(self._warnings_state)
  149. def test_textfield_hash(self):
  150. """
  151. Regression test for #10034: the hash generation function should ignore
  152. leading/trailing whitespace so as to be friendly to broken browsers that
  153. submit it (usually in textareas).
  154. """
  155. f1 = HashTestForm({'name': 'joe', 'bio': 'Nothing notable.'})
  156. f2 = HashTestForm({'name': ' joe', 'bio': 'Nothing notable. '})
  157. hash1 = utils.security_hash(None, f1)
  158. hash2 = utils.security_hash(None, f2)
  159. self.assertEqual(hash1, hash2)
  160. def test_empty_permitted(self):
  161. """
  162. Regression test for #10643: the security hash should allow forms with
  163. empty_permitted = True, or forms where data has not changed.
  164. """
  165. f1 = HashTestBlankForm({})
  166. f2 = HashTestForm({}, empty_permitted=True)
  167. hash1 = utils.security_hash(None, f1)
  168. hash2 = utils.security_hash(None, f2)
  169. self.assertEqual(hash1, hash2)
  170. class FormHmacTests(unittest.TestCase):
  171. """
  172. Same as SecurityHashTests, but with form_hmac
  173. """
  174. def test_textfield_hash(self):
  175. """
  176. Regression test for #10034: the hash generation function should ignore
  177. leading/trailing whitespace so as to be friendly to broken browsers that
  178. submit it (usually in textareas).
  179. """
  180. f1 = HashTestForm({'name': 'joe', 'bio': 'Nothing notable.'})
  181. f2 = HashTestForm({'name': ' joe', 'bio': 'Nothing notable. '})
  182. hash1 = utils.form_hmac(f1)
  183. hash2 = utils.form_hmac(f2)
  184. self.assertEqual(hash1, hash2)
  185. def test_empty_permitted(self):
  186. """
  187. Regression test for #10643: the security hash should allow forms with
  188. empty_permitted = True, or forms where data has not changed.
  189. """
  190. f1 = HashTestBlankForm({})
  191. f2 = HashTestForm({}, empty_permitted=True)
  192. hash1 = utils.form_hmac(f1)
  193. hash2 = utils.form_hmac(f2)
  194. self.assertEqual(hash1, hash2)
  195. #
  196. # FormWizard tests
  197. #
  198. class TestWizardClass(FormWizard):
  199. def get_template(self, step):
  200. return 'forms/wizard.html'
  201. def done(self, request, cleaned_data):
  202. return http.HttpResponse(success_string)
  203. class DummyRequest(http.HttpRequest):
  204. def __init__(self, POST=None):
  205. super(DummyRequest, self).__init__()
  206. self.method = POST and "POST" or "GET"
  207. if POST is not None:
  208. self.POST.update(POST)
  209. self._dont_enforce_csrf_checks = True
  210. class WizardTests(FormToolsTestCase):
  211. urls = 'django.contrib.formtools.tests.urls'
  212. input_re = re.compile('name="([^"]+)" value="([^"]+)"')
  213. wizard_step_data = (
  214. {
  215. '0-name': 'Pony',
  216. '0-thirsty': '2',
  217. },
  218. {
  219. '1-address1': '123 Main St',
  220. '1-address2': 'Djangoland',
  221. },
  222. {
  223. '2-random_crap': 'blah blah',
  224. }
  225. )
  226. def setUp(self):
  227. super(WizardTests, self).setUp()
  228. # Use a known SECRET_KEY to make security_hash tests deterministic
  229. self.old_SECRET_KEY = settings.SECRET_KEY
  230. settings.SECRET_KEY = "123"
  231. def tearDown(self):
  232. super(WizardTests, self).tearDown()
  233. settings.SECRET_KEY = self.old_SECRET_KEY
  234. def test_step_starts_at_zero(self):
  235. """
  236. step should be zero for the first form
  237. """
  238. response = self.client.get('/wizard1/')
  239. self.assertEqual(0, response.context['step0'])
  240. def test_step_increments(self):
  241. """
  242. step should be incremented when we go to the next page
  243. """
  244. response = self.client.post('/wizard1/', {"0-field":"test", "wizard_step":"0"})
  245. self.assertEqual(1, response.context['step0'])
  246. def test_bad_hash(self):
  247. """
  248. Form should not advance if the hash is missing or bad
  249. """
  250. response = self.client.post('/wizard1/',
  251. {"0-field":"test",
  252. "1-field":"test2",
  253. "wizard_step": "1"})
  254. self.assertEqual(0, response.context['step0'])
  255. def test_good_hash(self):
  256. """
  257. Form should advance if the hash is present and good, as calculated using
  258. current method.
  259. """
  260. data = {"0-field": "test",
  261. "1-field": "test2",
  262. "hash_0": "7e9cea465f6a10a6fb47fcea65cb9a76350c9a5c",
  263. "wizard_step": "1"}
  264. response = self.client.post('/wizard1/', data)
  265. self.assertEqual(2, response.context['step0'])
  266. def test_11726(self):
  267. """
  268. Regression test for ticket #11726.
  269. Wizard should not raise Http404 when steps are added dynamically.
  270. """
  271. reached = [False]
  272. that = self
  273. class WizardWithProcessStep(TestWizardClass):
  274. def process_step(self, request, form, step):
  275. if step == 0:
  276. if self.num_steps() < 2:
  277. self.form_list.append(WizardPageTwoForm)
  278. if step == 1:
  279. that.assertTrue(isinstance(form, WizardPageTwoForm))
  280. reached[0] = True
  281. wizard = WizardWithProcessStep([WizardPageOneForm])
  282. data = {"0-field": "test",
  283. "1-field": "test2",
  284. "hash_0": "7e9cea465f6a10a6fb47fcea65cb9a76350c9a5c",
  285. "wizard_step": "1"}
  286. wizard(DummyRequest(POST=data))
  287. self.assertTrue(reached[0])
  288. data = {"0-field": "test",
  289. "1-field": "test2",
  290. "hash_0": "7e9cea465f6a10a6fb47fcea65cb9a76350c9a5c",
  291. "hash_1": "d5b434e3934cc92fee4bd2964c4ebc06f81d362d",
  292. "wizard_step": "2"}
  293. self.assertRaises(http.Http404, wizard, DummyRequest(POST=data))
  294. def test_14498(self):
  295. """
  296. Regression test for ticket #14498. All previous steps' forms should be
  297. validated.
  298. """
  299. reached = [False]
  300. that = self
  301. class WizardWithProcessStep(TestWizardClass):
  302. def process_step(self, request, form, step):
  303. that.assertTrue(hasattr(form, 'cleaned_data'))
  304. reached[0] = True
  305. wizard = WizardWithProcessStep([WizardPageOneForm,
  306. WizardPageTwoForm,
  307. WizardPageThreeForm])
  308. data = {"0-field": "test",
  309. "1-field": "test2",
  310. "hash_0": "7e9cea465f6a10a6fb47fcea65cb9a76350c9a5c",
  311. "wizard_step": "1"}
  312. wizard(DummyRequest(POST=data))
  313. self.assertTrue(reached[0])
  314. def test_14576(self):
  315. """
  316. Regression test for ticket #14576.
  317. The form of the last step is not passed to the done method.
  318. """
  319. reached = [False]
  320. that = self
  321. class Wizard(TestWizardClass):
  322. def done(self, request, form_list):
  323. reached[0] = True
  324. that.assertTrue(len(form_list) == 2)
  325. wizard = Wizard([WizardPageOneForm,
  326. WizardPageTwoForm])
  327. data = {"0-field": "test",
  328. "1-field": "test2",
  329. "hash_0": "7e9cea465f6a10a6fb47fcea65cb9a76350c9a5c",
  330. "wizard_step": "1"}
  331. wizard(DummyRequest(POST=data))
  332. self.assertTrue(reached[0])
  333. def test_15075(self):
  334. """
  335. Regression test for ticket #15075. Allow modifying wizard's form_list
  336. in process_step.
  337. """
  338. reached = [False]
  339. that = self
  340. class WizardWithProcessStep(TestWizardClass):
  341. def process_step(self, request, form, step):
  342. if step == 0:
  343. self.form_list[1] = WizardPageTwoAlternativeForm
  344. if step == 1:
  345. that.assertTrue(isinstance(form, WizardPageTwoAlternativeForm))
  346. reached[0] = True
  347. wizard = WizardWithProcessStep([WizardPageOneForm,
  348. WizardPageTwoForm,
  349. WizardPageThreeForm])
  350. data = {"0-field": "test",
  351. "1-field": "test2",
  352. "hash_0": "7e9cea465f6a10a6fb47fcea65cb9a76350c9a5c",
  353. "wizard_step": "1"}
  354. wizard(DummyRequest(POST=data))
  355. self.assertTrue(reached[0])
  356. def grab_field_data(self, response):
  357. """
  358. Pull the appropriate field data from the context to pass to the next wizard step
  359. """
  360. previous_fields = response.context['previous_fields']
  361. fields = {'wizard_step': response.context['step0']}
  362. def grab(m):
  363. fields[m.group(1)] = m.group(2)
  364. return ''
  365. self.input_re.sub(grab, previous_fields)
  366. return fields
  367. def check_wizard_step(self, response, step_no):
  368. """
  369. Helper function to test each step of the wizard
  370. - Make sure the call succeeded
  371. - Make sure response is the proper step number
  372. - return the result from the post for the next step
  373. """
  374. step_count = len(self.wizard_step_data)
  375. self.assertEqual(response.status_code, 200)
  376. self.assertContains(response, 'Step %d of %d' % (step_no, step_count))
  377. data = self.grab_field_data(response)
  378. data.update(self.wizard_step_data[step_no - 1])
  379. return self.client.post('/wizard2/', data)
  380. def test_9473(self):
  381. response = self.client.get('/wizard2/')
  382. for step_no in range(1, len(self.wizard_step_data) + 1):
  383. response = self.check_wizard_step(response, step_no)