/src/web/browser.py
Python | 236 lines | 207 code | 17 blank | 12 comment | 4 complexity | ca0cb87b51e9bbd558e66ec16a68fe2a MD5 | raw file
- """Browser to test web applications.
- (from web.py)
- """
- from utils import re_compile
- from net import htmlunquote
- import httplib, urllib, urllib2
- import copy
- from StringIO import StringIO
- DEBUG = False
- __all__ = [
- "BrowserError",
- "Browser", "AppBrowser",
- "AppHandler"
- ]
- class BrowserError(Exception):
- pass
- class Browser:
- def __init__(self):
- import cookielib
- self.cookiejar = cookielib.CookieJar()
- self._cookie_processor = urllib2.HTTPCookieProcessor(self.cookiejar)
- self.form = None
- self.url = "http://0.0.0.0:8080/"
- self.path = "/"
-
- self.status = None
- self.data = None
- self._response = None
- self._forms = None
- def reset(self):
- """Clears all cookies and history."""
- self.cookiejar.clear()
- def build_opener(self):
- """Builds the opener using urllib2.build_opener.
- Subclasses can override this function to prodive custom openers.
- """
- return urllib2.build_opener()
- def do_request(self, req):
- if DEBUG:
- print 'requesting', req.get_method(), req.get_full_url()
- opener = self.build_opener()
- opener.add_handler(self._cookie_processor)
- try:
- self._response = opener.open(req)
- except urllib2.HTTPError, e:
- self._response = e
- self.url = self._response.geturl()
- self.path = urllib2.Request(self.url).get_selector()
- self.data = self._response.read()
- self.status = self._response.code
- self._forms = None
- self.form = None
- return self.get_response()
- def open(self, url, data=None, headers={}):
- """Opens the specified url."""
- url = urllib.basejoin(self.url, url)
- req = urllib2.Request(url, data, headers)
- return self.do_request(req)
- def show(self):
- """Opens the current page in real web browser."""
- f = open('page.html', 'w')
- f.write(self.data)
- f.close()
- import webbrowser, os
- url = 'file://' + os.path.abspath('page.html')
- webbrowser.open(url)
- def get_response(self):
- """Returns a copy of the current response."""
- return urllib.addinfourl(StringIO(self.data), self._response.info(), self._response.geturl())
- def get_soup(self):
- """Returns beautiful soup of the current document."""
- import BeautifulSoup
- return BeautifulSoup.BeautifulSoup(self.data)
- def get_text(self, e=None):
- """Returns content of e or the current document as plain text."""
- e = e or self.get_soup()
- return ''.join([htmlunquote(c) for c in e.recursiveChildGenerator() if isinstance(c, unicode)])
- def _get_links(self):
- soup = self.get_soup()
- return [a for a in soup.findAll(name='a')]
-
- def get_links(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
- """Returns all links in the document."""
- return self._filter_links(self._get_links(),
- text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
- def follow_link(self, link=None, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
- if link is None:
- links = self._filter_links(self.get_links(),
- text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
- link = links and links[0]
-
- if link:
- return self.open(link['href'])
- else:
- raise BrowserError("No link found")
-
- def find_link(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
- links = self._filter_links(self.get_links(),
- text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
- return links and links[0] or None
-
- def _filter_links(self, links,
- text=None, text_regex=None,
- url=None, url_regex=None,
- predicate=None):
- predicates = []
- if text is not None:
- predicates.append(lambda link: link.string == text)
- if text_regex is not None:
- predicates.append(lambda link: re_compile(text_regex).search(link.string or ''))
- if url is not None:
- predicates.append(lambda link: link.get('href') == url)
- if url_regex is not None:
- predicates.append(lambda link: re_compile(url_regex).search(link.get('href', '')))
- if predicate:
- predicate.append(predicate)
- def f(link):
- for p in predicates:
- if not p(link):
- return False
- return True
- return [link for link in links if f(link)]
- def get_forms(self):
- """Returns all forms in the current document.
- The returned form objects implement the ClientForm.HTMLForm interface.
- """
- if self._forms is None:
- import ClientForm
- self._forms = ClientForm.ParseResponse(self.get_response(), backwards_compat=False)
- return self._forms
- def select_form(self, name=None, predicate=None, index=0):
- """Selects the specified form."""
- forms = self.get_forms()
- if name is not None:
- forms = [f for f in forms if f.name == name]
- if predicate:
- forms = [f for f in forms if predicate(f)]
-
- if forms:
- self.form = forms[index]
- return self.form
- else:
- raise BrowserError("No form selected.")
-
- def submit(self, **kw):
- """submits the currently selected form."""
- if self.form is None:
- raise BrowserError("No form selected.")
- req = self.form.click(**kw)
- return self.do_request(req)
- def __getitem__(self, key):
- return self.form[key]
- def __setitem__(self, key, value):
- self.form[key] = value
- class AppBrowser(Browser):
- """Browser interface to test web.py apps.
-
- b = AppBrowser(app)
- b.open('/')
- b.follow_link(text='Login')
-
- b.select_form(name='login')
- b['username'] = 'joe'
- b['password'] = 'secret'
- b.submit()
- assert b.path == '/'
- assert 'Welcome joe' in b.get_text()
- """
- def __init__(self, app):
- Browser.__init__(self)
- self.app = app
- def build_opener(self):
- return urllib2.build_opener(AppHandler(self.app))
- class AppHandler(urllib2.HTTPHandler):
- """urllib2 handler to handle requests using web.py application."""
- handler_order = 100
- def __init__(self, app):
- self.app = app
- def http_open(self, req):
- result = self.app.request(
- localpart=req.get_selector(),
- method=req.get_method(),
- host=req.get_host(),
- data=req.get_data(),
- headers=dict(req.header_items()),
- https=req.get_type() == "https"
- )
- return self._make_response(result, req.get_full_url())
- def https_open(self, req):
- return self.http_open(req)
-
- try:
- https_request = urllib2.HTTPHandler.do_request_
- except AttributeError:
- # for python 2.3
- pass
- def _make_response(self, result, url):
- data = "\r\n".join(["%s: %s" % (k, v) for k, v in result.header_items])
- headers = httplib.HTTPMessage(StringIO(data))
- response = urllib.addinfourl(StringIO(result.data), headers, url)
- code, msg = result.status.split(None, 1)
- response.code, response.msg = int(code), msg
- return response