__init__.py 15 KB

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