PageRenderTime 52ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/twilltestserver.py

https://github.com/SMFOSS/twill
Python | 557 lines | 530 code | 10 blank | 17 comment | 0 complexity | e45ed724a99a024142ec63bc0254f25f MD5 | raw file
  1. #! /usr/local/bin/python2.3
  2. """
  3. Quixote test app for twill.
  4. """
  5. import sys
  6. import os
  7. import pkg_resources
  8. pkg_resources.require('Quixote>=2.4')
  9. from quixote.publish import Publisher
  10. from quixote.errors import AccessError
  11. from quixote.session import Session, SessionManager
  12. from quixote.directory import Directory, AccessControlled
  13. from quixote import get_user, get_session, get_session_manager, get_path, \
  14. redirect, get_request, get_response
  15. from quixote.form import widget
  16. import base64
  17. class AlwaysSession(Session):
  18. def __init__(self, session_id):
  19. Session.__init__(self, session_id)
  20. self.n = 0
  21. def has_info(self):
  22. """
  23. Always save.
  24. """
  25. return True
  26. is_dirty = has_info
  27. from quixote.errors import AccessError
  28. class UnauthorizedError(AccessError):
  29. """
  30. The request requires user authentication.
  31. This subclass of AccessError sends a 401 instead of a 403,
  32. hinting that the client should try again with authentication.
  33. (from http://www.quixote.ca/qx/HttpBasicAuthentication)
  34. """
  35. status_code = 401
  36. title = "Unauthorized"
  37. description = "You are not authorized to access this resource."
  38. def __init__(self, realm='Protected', public_msg=None, private_msg=None):
  39. self.realm = realm
  40. AccessError.__init__(self, public_msg, private_msg)
  41. def format(self):
  42. request = get_request()
  43. request.response.set_header('WWW-Authenticate',
  44. 'Basic realm="%s"' % self.realm)
  45. return AccessError.format(self)
  46. def create_publisher():
  47. """
  48. Create a publisher for TwillTest, with session management added on.
  49. """
  50. session_manager = SessionManager(session_class=AlwaysSession)
  51. return Publisher(TwillTest(),
  52. session_manager=session_manager)
  53. def message(session):
  54. return """\
  55. <html>
  56. <head>
  57. <title>Hello, world!</title>
  58. </head>
  59. <body>
  60. Hello, world!
  61. <p>
  62. These are the twill tests.
  63. <p>
  64. Your session ID is %s; this is visit #%d.
  65. <p>
  66. You are logged in as "%s".
  67. <p>
  68. <a href="./increment">increment</a> | <a href="./incrementfail">incrementfail</a>
  69. <p>
  70. <a href="logout">log out</a>
  71. <p>
  72. (<a href="test spaces">test spaces</a> / <a href="test_spaces">test spaces2</a>)
  73. </body>
  74. </html>
  75. """ % (session.id, session.n, session.user)
  76. class TwillTest(Directory):
  77. _q_exports = ['logout', 'increment', 'incrementfail', "", 'restricted',
  78. 'login', ('test spaces', 'test_spaces'), 'test_spaces',
  79. 'simpleform', 'upload_file', 'http_auth', 'formpostredirect',
  80. 'exit', 'multisubmitform', "exception", "plaintext",
  81. "testform", "testformaction",
  82. "test_refresh", "test_refresh2", "test_refresh3",
  83. "test_checkbox", "test_simple_checkbox","echo",
  84. "test_checkboxes", 'test_global_form',
  85. 'tidy_fixable_html', 'BS_fixable_html', 'unfixable_html',
  86. 'effed_up_forms', 'effed_up_forms2', 'broken_linktext',
  87. 'exit', 'display_post', 'display_environ']
  88. def test_global_form(self):
  89. return """
  90. <html>
  91. <head>
  92. <title>Broken</title>
  93. </head>
  94. <body>
  95. <div>
  96. <input name="global_form_entry" type="text">
  97. </div>
  98. <form name="login" method="post">
  99. <input type=text name=hello>
  100. <input type=submit>
  101. </form>
  102. <form name="login" method="post" action="http://iorich.caltech.edu:8080/display_post">
  103. <input type=text name=hello>
  104. <input type=submit>
  105. </form>
  106. </body>
  107. </html>
  108. """
  109. def display_post(self):
  110. s = ""
  111. request = get_request()
  112. for k, v in request.form.items():
  113. s += "k: '''%s''' : '''%s'''<p>\n" % (k, v,)
  114. return s
  115. def display_environ(self):
  116. s = ""
  117. request = get_request()
  118. for k, v in request.environ.items():
  119. s += "k: '''%s''' : '''%s'''<p>\n" % (k, v,)
  120. return s
  121. def exit(self):
  122. raise SystemExit
  123. def __init__(self):
  124. self.restricted = Restricted()
  125. self.http_auth = HttpAuthRestricted()
  126. def _q_index(self):
  127. session = get_session()
  128. return message(session)
  129. def tidy_fixable_html(self):
  130. return """\
  131. <!-- fixed by tidy, but not parseable otherwise: 0 forms on fail. -->
  132. <form>
  133. <input type=text name=blah value=thus>
  134. """
  135. def BS_fixable_html(self):
  136. return """\
  137. <!-- tidy errors out on this, but it can be parsed by BS. -->
  138. <form>
  139. <table>
  140. <tr><td>
  141. <input name='broken'>
  142. </td>
  143. </form>
  144. </tr>
  145. </form>
  146. """
  147. def unfixable_html(self):
  148. return """\
  149. <!-- tidy errors out on this, and it cannot be parsed by BS. -->
  150. <table>
  151. <tr><td>
  152. <input name='broken'>
  153. </td>
  154. </form>
  155. </tr>
  156. </form>
  157. """
  158. def effed_up_forms(self):
  159. return """\
  160. <font>
  161. <INPUT>
  162. <FORM>
  163. <input type="blah">
  164. </form>
  165. """
  166. def effed_up_forms2(self):
  167. return """\
  168. <div id="loginform">
  169. <form method="post" name="loginform" action="ChkLogin">
  170. <h3>ARINC Direct Login</h3>
  171. <br/>
  172. <strong>User ID</strong><br/>
  173. <input name="username" id="username" type="text" style="width:80%;"><br/>
  174. <strong>Password</strong><br/>
  175. <input name="password" type="password" style="width:80%;"><br/>
  176. <div id="buttonbar">
  177. <input value="Login" name="login" class="button" type="submit">
  178. </div>
  179. </form>
  180. </div>
  181. """
  182. def broken_linktext(self):
  183. return """
  184. <a href="/">
  185. <span>some text</span>
  186. </a>
  187. """
  188. def test_refresh(self):
  189. return """\
  190. <meta http-equiv="refresh" content="2; url=./login">
  191. hello, world.
  192. """
  193. def test_refresh2(self):
  194. return """\
  195. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  196. <html>
  197. <head>
  198. <title>o2.ie</title>
  199. <meta http-equiv="refresh" content="0;URL=/login">
  200. <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
  201. </head>
  202. <body>
  203. </body>
  204. </html>
  205. hello, world.
  206. """
  207. def test_refresh3(self):
  208. """
  209. check for situation where given URL is quoted.
  210. """
  211. return """\
  212. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  213. <html>
  214. <head>
  215. <title>o2.ie</title>
  216. <meta http-equiv="refresh" content="0;'URL=/login'">
  217. <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
  218. </head>
  219. <body>
  220. </body>
  221. </html>
  222. hello, world.
  223. """
  224. def exception(self):
  225. raise Exception("500 error -- fail out!")
  226. def test_spaces(self):
  227. return "success"
  228. def increment(self):
  229. session = get_session()
  230. session.n += 1
  231. return message(session)
  232. def incrementfail(self):
  233. session = get_session()
  234. session.n += 1
  235. raise Exception(message(session))
  236. def login(self):
  237. request = get_request()
  238. username_widget = widget.StringWidget(name='username',
  239. value='')
  240. submit_widget = widget.SubmitWidget(name='submit',
  241. value='submit me')
  242. submit_widget2 = widget.SubmitWidget(name='nosubmit2',
  243. value="don't submit")
  244. if request.form:
  245. assert not submit_widget2.parse(request)
  246. username = username_widget.parse(request)
  247. if username:
  248. session = get_session()
  249. session.set_user(username)
  250. return redirect('./')
  251. image_submit = '''<input type=image name='submit you' src=DNE.gif>'''
  252. return "<form method=POST>Log in: %s<p>%s<p>%s<p>%s</form>" % \
  253. (username_widget.render(),
  254. submit_widget2.render(),
  255. submit_widget.render(),
  256. image_submit)
  257. def simpleform(self):
  258. """
  259. no submit button...
  260. """
  261. request = get_request()
  262. w1 = widget.StringWidget(name='n', value='')
  263. w2 = widget.StringWidget(name='n2', value='')
  264. return "%s %s <form method=POST><input type=text name=n><input type=text name=n2></form>" % (w1.parse(request), w2.parse(request),)
  265. def multisubmitform(self):
  266. request = get_request()
  267. submit1 = widget.SubmitWidget('sub_a', value='sub_a')
  268. submit2 = widget.SubmitWidget('sub_b', value='sub_b')
  269. s = ""
  270. if request.form:
  271. used = False
  272. if submit1.parse(request):
  273. used = True
  274. s += "used_sub_a"
  275. if submit2.parse(request):
  276. used = True
  277. s += "used_sub_b"
  278. if not used:
  279. assert 0
  280. # print out the referer, too.
  281. referer = request.environ.get('HTTP_REFERER')
  282. if referer:
  283. s += "<p>referer: %s" % (referer,)
  284. return "<form method=POST>%s %s %s</form>" % (s,
  285. submit1.render(),
  286. submit2.render())
  287. def testformaction(self):
  288. request = get_request()
  289. keys = [ k for k in request.form.keys() if request.form[k] ]
  290. keys.sort()
  291. return "==" + " AND ".join(keys) + "=="
  292. def testform(self):
  293. request = get_request()
  294. s = ""
  295. if not request.form:
  296. s = "NO FORM"
  297. if request.form and request.form.has_key('selecttest'):
  298. vals = request.form['selecttest']
  299. if isinstance(vals, str):
  300. vals = [vals,]
  301. s += "SELECTTEST: ==%s==<p>" % " AND ".join(vals,)
  302. if request.form:
  303. l = []
  304. for name in ('item', 'item_a', 'item_b', 'item_c'):
  305. if request.form.get(name):
  306. val = request.form[name]
  307. l.append('%s=%s' % (name, val))
  308. s += "NAMETEST: ==%s==<p>" % " AND ".join(l)
  309. return """\
  310. %s
  311. <form method=POST id=the_form>
  312. <select name=selecttest multiple>
  313. <option> val
  314. <option value='selvalue1'> value1 </option>
  315. <option value='selvalue2'> value2 </option>
  316. <option value='selvalue3'> value3 </option>
  317. <option value='test.value3'> testme.val </option>
  318. <option value=Test.Value4> testme4.val </option>
  319. </select>
  320. <input type=text name=item>
  321. <input type=text name=item_a>
  322. <input type=text name=item_b>
  323. <input type=text name=item_c>
  324. <input type=text id=some_id>
  325. <input type=submit value=post id=submit_button>
  326. </form>
  327. """ % (s,)
  328. def test_checkbox(self):
  329. request = get_request()
  330. s = ""
  331. if request.form and request.form.has_key('checkboxtest'):
  332. val = request.form['checkboxtest']
  333. if not isinstance(val, str):
  334. val = val[0]
  335. s += "CHECKBOXTEST: ==%s==<p>" % val
  336. return """\
  337. %s
  338. <form method=POST>
  339. <input type="checkbox" name="checkboxtest" value="True">
  340. <input type="hidden" name="checkboxtest" value="False">
  341. <input type=submit value=post>
  342. </form>
  343. """ % (s,)
  344. def test_checkboxes(self):
  345. request = get_request()
  346. s = ""
  347. if request.form and request.form.has_key('checkboxtest'):
  348. val = request.form['checkboxtest']
  349. if not isinstance(val, str):
  350. val = ','.join(val)
  351. s += "CHECKBOXTEST: ==%s==<p>" % val
  352. return """\
  353. %s
  354. <form method=POST>
  355. <input type="checkbox" name="checkboxtest" value="one">
  356. <input type="checkbox" name="checkboxtest" value="two">
  357. <input type="checkbox" name="checkboxtest" value="three">
  358. <input type=submit value=post>
  359. </form>
  360. """ % (s,)
  361. def test_simple_checkbox(self):
  362. request = get_request()
  363. s = ""
  364. if request.form and request.form.has_key('checkboxtest'):
  365. val = request.form['checkboxtest']
  366. if not isinstance(val, str):
  367. val = val[0]
  368. s += "CHECKBOXTEST: ==%s==<p>" % val
  369. return """\
  370. %s
  371. <form method=POST>
  372. <input type="checkbox" name="checkboxtest">
  373. <input type=submit value=post>
  374. </form>
  375. """ % (s,)
  376. def formpostredirect(self):
  377. """
  378. Test redirect after a form POST. This tests a specific bug in
  379. mechanize...
  380. """
  381. request = get_request()
  382. if not request.form:
  383. return """\
  384. <form method=POST enctype=multipart/form-data>
  385. <input type=text name=test>
  386. <input type=submit value=submit name=submit>
  387. </form>
  388. """
  389. redirect(get_path(1) + '/')
  390. def logout(self):
  391. # expire session
  392. session_manager = get_session_manager()
  393. session_manager.expire_session()
  394. # redirect to index page.
  395. return redirect(get_path(1) + '/')
  396. def plaintext(self):
  397. response = get_response()
  398. response.set_content_type("text/plain")
  399. return "hello, world"
  400. def echo(self):
  401. request = get_request()
  402. if request.form and request.form.has_key('q'):
  403. return request.form['q']
  404. return ""
  405. def upload_file(self):
  406. request = get_request()
  407. if request.form:
  408. contents = request.form['upload'].fp.read()
  409. return contents
  410. else:
  411. return "<form enctype=multipart/form-data method=POST> <input type=file name=upload> <input type=submit value=submit> </form>"
  412. def exit(self):
  413. os._exit(0)
  414. class Restricted(AccessControlled, Directory):
  415. _q_exports = [""]
  416. def _q_access(self):
  417. session = get_session()
  418. if not session.user:
  419. raise AccessError("you must have a username")
  420. def _q_index(self):
  421. return "you made it!"
  422. class HttpAuthRestricted(AccessControlled, Directory):
  423. _q_exports = [""]
  424. def _q_access(self):
  425. r = get_request()
  426. print '======================== NEW REQUEST'
  427. for k, v in r.environ.items():
  428. print '***', k, ':', v
  429. ha = r.get_environ('HTTP_AUTHORIZATION', None)
  430. if ha:
  431. auth_type, auth_string = ha.split()
  432. login, passwd = base64.decodestring(auth_string).split(':')
  433. if login == 'test' and passwd == 'password':
  434. return
  435. raise UnauthorizedError
  436. def _q_index(self):
  437. return "you made it!"
  438. ####
  439. if __name__ == '__main__':
  440. from quixote.server.simple_server import run
  441. port = int(os.environ.get('TWILL_TEST_PORT', '8080'))
  442. print 'starting twilltestserver on port %d.' % (port,)
  443. try:
  444. run(create_publisher, port=port)
  445. except KeyboardInterrupt:
  446. pass