[3154] | 1 | import unittest |
---|
[3270] | 2 | import doctest |
---|
[3154] | 3 | import re |
---|
[2012] | 4 | from urllib import urlencode |
---|
| 5 | from StringIO import StringIO |
---|
[1998] | 6 | |
---|
[3154] | 7 | from Products.PloneTestCase.PloneTestCase import portal_owner |
---|
| 8 | from Products.PloneTestCase.PloneTestCase import default_password |
---|
| 9 | |
---|
[3270] | 10 | from quintagroup.plonecaptchas.tests.base import FunctionalTestCase, ztc |
---|
| 11 | from quintagroup.plonecaptchas.config import PRODUCT_NAME, HAS_APP_DISCUSSION |
---|
[3154] | 12 | |
---|
| 13 | from quintagroup.captcha.core.tests.testWidget import IMAGE_PATT, NOT_VALID |
---|
| 14 | from quintagroup.captcha.core.tests.testWidget import addTestLayer |
---|
| 15 | from quintagroup.captcha.core.tests.base import testPatch |
---|
| 16 | from quintagroup.captcha.core.utils import getWord, decrypt, parseKey |
---|
| 17 | |
---|
[2014] | 18 | from plone.app.controlpanel.security import ISecuritySchema |
---|
| 19 | |
---|
[3160] | 20 | # BBB for plone v<3.1, where plone.protect not used yet |
---|
[2021] | 21 | PROTECT_SUPPORT = True |
---|
| 22 | try: |
---|
| 23 | from plone import protect |
---|
[3154] | 24 | # pyflakes fix (pyflakes message: 'protect' imported but unused) |
---|
| 25 | protect |
---|
[2021] | 26 | except ImportError: |
---|
| 27 | PROTECT_SUPPORT = False |
---|
| 28 | |
---|
[2008] | 29 | # USE PATCH FROM quintagroup.captcha.core |
---|
| 30 | # patch to use test images and dictionary |
---|
| 31 | testPatch() |
---|
[1998] | 32 | |
---|
[3160] | 33 | |
---|
[2017] | 34 | class TestFormMixin(FunctionalTestCase): |
---|
[2008] | 35 | |
---|
[2496] | 36 | formkey_key = "key" |
---|
| 37 | formkey_hashkey = "hashkey" |
---|
| 38 | |
---|
[1998] | 39 | def afterSetUp(self): |
---|
| 40 | self.loginAsPortalOwner() |
---|
| 41 | self.addProduct(PRODUCT_NAME) |
---|
[2008] | 42 | # Add test_captcha layer from quintagroup.captcah.core |
---|
| 43 | addTestLayer(self) |
---|
[2012] | 44 | # Prepare form data |
---|
[3160] | 45 | self.basic_auth = ':'.join((portal_owner, default_password)) |
---|
[2012] | 46 | self.form_url = '' |
---|
| 47 | self.form_method = "POST" |
---|
| 48 | self.hasAuthenticator = False |
---|
| 49 | self.form_data = self.getFormData() |
---|
| 50 | # Prepare captcha related test data |
---|
[1998] | 51 | self.captcha_key = self.portal.captcha_key |
---|
[2009] | 52 | self.hashkey = self.portal.getCaptcha() |
---|
[2496] | 53 | self.form_data[self.formkey_hashkey] = self.hashkey |
---|
| 54 | self.form_data[self.formkey_key] = '' |
---|
[1998] | 55 | |
---|
[2012] | 56 | def getFormData(self): |
---|
[2008] | 57 | raise NotImplementedError( |
---|
[2012] | 58 | "getFormData not implemented") |
---|
[3160] | 59 | |
---|
[2012] | 60 | def publishForm(self): |
---|
| 61 | stdin_data = None |
---|
| 62 | form_url = self.portal.absolute_url(1) + self.form_url |
---|
| 63 | # Prepare form data |
---|
[2021] | 64 | if PROTECT_SUPPORT and self.hasAuthenticator: |
---|
[2012] | 65 | self.form_data['_authenticator'] = self._getauth() |
---|
| 66 | form_data = urlencode(self.form_data) |
---|
| 67 | if self.form_method.upper() == 'GET': |
---|
| 68 | form_url += "?%s" % form_data |
---|
| 69 | else: |
---|
| 70 | stdin_data = StringIO(form_data) |
---|
| 71 | # Publish form and get response |
---|
| 72 | response = self.publish(form_url, self.basic_auth, |
---|
[3609] | 73 | request_method=self.form_method, |
---|
| 74 | stdin=stdin_data) |
---|
[2012] | 75 | return response |
---|
[2008] | 76 | |
---|
[2012] | 77 | def _getauth(self): |
---|
| 78 | # Fix authenticator for the form |
---|
| 79 | authenticator = self.portal.restrictedTraverse("@@authenticator") |
---|
| 80 | html = authenticator.authenticator() |
---|
| 81 | handle = re.search('value="(.*)"', html).groups()[0] |
---|
| 82 | return handle |
---|
| 83 | |
---|
[2494] | 84 | def elog(self, name="", response=""): |
---|
| 85 | open("/tmp/test.%s.html" % name, "w").write(response) |
---|
| 86 | logs = self.portal.error_log.getLogEntries() |
---|
[3160] | 87 | if len(logs) > 0: |
---|
[2494] | 88 | i = 0 |
---|
| 89 | while logs: |
---|
| 90 | l = logs.pop() |
---|
[3160] | 91 | i += 1 |
---|
| 92 | open("/tmp/test.%s.error.%d.html" % (l["id"], i), |
---|
[3609] | 93 | "w").write(l["tb_html"]) |
---|
[2494] | 94 | |
---|
[1998] | 95 | def testImage(self): |
---|
[2045] | 96 | self.form_data = {} |
---|
| 97 | self.form_method = "GET" |
---|
[2012] | 98 | response = self.publishForm().getBody() |
---|
[3160] | 99 | patt = re.compile(IMAGE_PATT % self.portal.absolute_url()) |
---|
[1998] | 100 | match_obj = patt.search(response) |
---|
[2494] | 101 | self.elog("image", response) |
---|
[1998] | 102 | img_url = match_obj.group(1) |
---|
| 103 | |
---|
[3160] | 104 | content_type = self.publish( |
---|
[3609] | 105 | '/plone' + img_url).getHeader('content-type') |
---|
[2008] | 106 | self.assertTrue(content_type.startswith('image'), |
---|
[3609] | 107 | "Wrong captcha image content type") |
---|
[2008] | 108 | |
---|
[1998] | 109 | def testSubmitRightCaptcha(self): |
---|
[3160] | 110 | key = getWord(int(parseKey(decrypt(self.captcha_key, |
---|
| 111 | self.hashkey))['key']) - 1) |
---|
[2496] | 112 | self.form_data[self.formkey_key] = key |
---|
[3160] | 113 | |
---|
[2012] | 114 | response = self.publishForm().getBody() |
---|
[2494] | 115 | self.elog("right", response) |
---|
[2008] | 116 | self.assertFalse(NOT_VALID.search(response)) |
---|
[1998] | 117 | |
---|
| 118 | def testSubmitWrongCaptcha(self): |
---|
[2496] | 119 | self.form_data[self.formkey_key] = 'wrong word' |
---|
[2012] | 120 | response = self.publishForm().getBody() |
---|
[2494] | 121 | self.elog("wrong", response) |
---|
[2008] | 122 | self.assertTrue(NOT_VALID.search(response)) |
---|
| 123 | |
---|
[1998] | 124 | def testSubmitRightCaptchaTwice(self): |
---|
[3160] | 125 | key = getWord(int(parseKey(decrypt(self.captcha_key, |
---|
| 126 | self.hashkey))['key']) - 1) |
---|
[2496] | 127 | self.form_data[self.formkey_key] = key |
---|
[1998] | 128 | |
---|
[2496] | 129 | response1 = self.publishForm().getBody() |
---|
[2494] | 130 | self.elog("right1", response1) |
---|
[2496] | 131 | response2 = self.publishForm().getBody() |
---|
| 132 | self.elog("right2", response2) |
---|
| 133 | self.assertTrue(NOT_VALID.search(response2)) |
---|
[2008] | 134 | |
---|
| 135 | |
---|
[2017] | 136 | class TestDiscussionForm(TestFormMixin): |
---|
[2008] | 137 | |
---|
| 138 | def afterSetUp(self): |
---|
[2017] | 139 | TestFormMixin.afterSetUp(self) |
---|
[2008] | 140 | self.portal.invokeFactory('Document', 'index_html') |
---|
| 141 | self.portal['index_html'].allowDiscussion(True) |
---|
[2013] | 142 | self.form_url = '/index_html/discussion_reply_form' |
---|
[3160] | 143 | |
---|
[2013] | 144 | def getFormData(self): |
---|
[3160] | 145 | return {'form.submitted': '1', |
---|
[2013] | 146 | 'subject': 'testing', |
---|
[2008] | 147 | 'Creator': portal_owner, |
---|
| 148 | 'body_text': 'Text in Comment', |
---|
[2013] | 149 | 'discussion_reply:method': 'Save', |
---|
[3160] | 150 | 'form.button.form_submit': 'Save'} |
---|
[2008] | 151 | |
---|
| 152 | |
---|
[2489] | 153 | class TestRegisterForm(TestFormMixin): |
---|
[2010] | 154 | |
---|
[2496] | 155 | formkey_key = "form.captcha" |
---|
| 156 | formkey_hashkey = "form..hashkey" |
---|
| 157 | |
---|
[2014] | 158 | def afterSetUp(self): |
---|
[2017] | 159 | TestFormMixin.afterSetUp(self) |
---|
[2014] | 160 | ISecuritySchema(self.portal).enable_self_reg = True |
---|
[2493] | 161 | self.portal._updateProperty('validate_email', False) |
---|
[2014] | 162 | self.hasAuthenticator = True |
---|
[2489] | 163 | self.form_url = '/@@register' |
---|
[2014] | 164 | self.basic_auth = ":" |
---|
| 165 | self.logout() |
---|
[2010] | 166 | |
---|
[2014] | 167 | def getFormData(self): |
---|
[3160] | 168 | return {"form.fullname": "Tester", |
---|
| 169 | "form.username": "tester", |
---|
| 170 | "form.email": "tester@test.com", |
---|
| 171 | "form.password": "123456", |
---|
| 172 | "form.password_ctl": "123456", |
---|
| 173 | 'form.actions.register': 'Register'} |
---|
[2010] | 174 | |
---|
| 175 | |
---|
[2017] | 176 | class TestSendtoForm(TestFormMixin): |
---|
[2016] | 177 | |
---|
| 178 | def afterSetUp(self): |
---|
[2017] | 179 | TestFormMixin.afterSetUp(self) |
---|
[2016] | 180 | self.portal.invokeFactory('Document', 'index_html') |
---|
| 181 | self.portal['index_html'].allowDiscussion(True) |
---|
| 182 | self.form_url = '/index_html/sendto_form' |
---|
[3160] | 183 | |
---|
[2016] | 184 | def getFormData(self): |
---|
[3160] | 185 | return {'form.submitted': '1', |
---|
| 186 | "send_to_address": "recipient@test.com", |
---|
| 187 | "send_from_address": "sender@test.com", |
---|
[2016] | 188 | 'comment': 'Text in Comment', |
---|
[3160] | 189 | 'form.button.Send': 'Save'} |
---|
[2016] | 190 | |
---|
[3160] | 191 | |
---|
[2456] | 192 | def send_patch(self, *args, **kwargs): |
---|
| 193 | """This patch prevent breakage on sending.""" |
---|
| 194 | |
---|
[3160] | 195 | |
---|
[2045] | 196 | class TestContactInfo(TestFormMixin): |
---|
[2016] | 197 | |
---|
[2045] | 198 | def afterSetUp(self): |
---|
| 199 | TestFormMixin.afterSetUp(self) |
---|
| 200 | # preparation to form correct working |
---|
[3160] | 201 | self.portal._updateProperty('email_from_address', 'manager@test.com') |
---|
[2045] | 202 | self.logout() |
---|
| 203 | self.form_url = '/contact-info' |
---|
[2456] | 204 | self.orig_mh_send = self.portal.MailHost.send |
---|
| 205 | self.portal.MailHost.send = send_patch |
---|
| 206 | |
---|
| 207 | def beforeTearDown(self): |
---|
| 208 | self.portal.MailHost.send = self.orig_mh_send |
---|
| 209 | |
---|
[2045] | 210 | def getFormData(self): |
---|
[3160] | 211 | return {'form.submitted': '1', |
---|
| 212 | "sender_fullname": "tester", |
---|
| 213 | "sender_from_address": "sender@test.com", |
---|
[2045] | 214 | 'subject': 'Subject', |
---|
[2458] | 215 | 'message': 'Message', |
---|
[3160] | 216 | 'form.button.Send': 'Save'} |
---|
[2045] | 217 | |
---|
| 218 | |
---|
[1998] | 219 | def test_suite(): |
---|
| 220 | suite = unittest.TestSuite() |
---|
[3270] | 221 | if HAS_APP_DISCUSSION: |
---|
| 222 | suite.addTest(unittest.TestSuite([ |
---|
| 223 | ztc.FunctionalDocFileSuite( |
---|
| 224 | 'discussion.txt', package='quintagroup.plonecaptchas.tests', |
---|
| 225 | test_class=FunctionalTestCase, globs=globals(), |
---|
| 226 | optionflags=doctest.REPORT_ONLY_FIRST_FAILURE | |
---|
[3609] | 227 | doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS), |
---|
| 228 | ])) |
---|
[3270] | 229 | |
---|
| 230 | else: |
---|
| 231 | suite.addTest(unittest.makeSuite(TestDiscussionForm)) |
---|
[2489] | 232 | suite.addTest(unittest.makeSuite(TestRegisterForm)) |
---|
[2016] | 233 | suite.addTest(unittest.makeSuite(TestSendtoForm)) |
---|
[2045] | 234 | suite.addTest(unittest.makeSuite(TestContactInfo)) |
---|
[1998] | 235 | return suite |
---|