1 """Browser to test web applications.
2 (from web.py)
3 """
4 from utils import re_compile
5 from net import htmlunquote
6
7 import httplib, urllib, urllib2
8 import copy
9 from StringIO import StringIO
10
11 DEBUG = False
12
13 __all__ = [
14 "BrowserError",
15 "Browser", "AppBrowser",
16 "AppHandler"
17 ]
18
21
24 import cookielib
25 self.cookiejar = cookielib.CookieJar()
26 self._cookie_processor = urllib2.HTTPCookieProcessor(self.cookiejar)
27 self.form = None
28
29 self.url = "http://0.0.0.0:8080/"
30 self.path = "/"
31
32 self.status = None
33 self.data = None
34 self._response = None
35 self._forms = None
36
38 """Clears all cookies and history."""
39 self.cookiejar.clear()
40
42 """Builds the opener using urllib2.build_opener.
43 Subclasses can override this function to prodive custom openers.
44 """
45 return urllib2.build_opener()
46
48 if DEBUG:
49 print 'requesting', req.get_method(), req.get_full_url()
50 opener = self.build_opener()
51 opener.add_handler(self._cookie_processor)
52 try:
53 self._response = opener.open(req)
54 except urllib2.HTTPError, e:
55 self._response = e
56
57 self.url = self._response.geturl()
58 self.path = urllib2.Request(self.url).get_selector()
59 self.data = self._response.read()
60 self.status = self._response.code
61 self._forms = None
62 self.form = None
63 return self.get_response()
64
65 - def open(self, url, data=None, headers={}):
66 """Opens the specified url."""
67 url = urllib.basejoin(self.url, url)
68 req = urllib2.Request(url, data, headers)
69 return self.do_request(req)
70
72 """Opens the current page in real web browser."""
73 f = open('page.html', 'w')
74 f.write(self.data)
75 f.close()
76
77 import webbrowser, os
78 url = 'file://' + os.path.abspath('page.html')
79 webbrowser.open(url)
80
82 """Returns a copy of the current response."""
83 return urllib.addinfourl(StringIO(self.data), self._response.info(), self._response.geturl())
84
86 """Returns beautiful soup of the current document."""
87 import BeautifulSoup
88 return BeautifulSoup.BeautifulSoup(self.data)
89
90 - def get_text(self, e=None):
91 """Returns content of e or the current document as plain text."""
92 e = e or self.get_soup()
93 return ''.join([htmlunquote(c) for c in e.recursiveChildGenerator() if isinstance(c, unicode)])
94
96 soup = self.get_soup()
97 return [a for a in soup.findAll(name='a')]
98
99 - def get_links(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
100 """Returns all links in the document."""
101 return self._filter_links(self._get_links(),
102 text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
103
104 - def follow_link(self, link=None, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
105 if link is None:
106 links = self._filter_links(self.get_links(),
107 text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
108 link = links and links[0]
109
110 if link:
111 return self.open(link['href'])
112 else:
113 raise BrowserError("No link found")
114
115 - def find_link(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
116 links = self._filter_links(self.get_links(),
117 text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
118 return links and links[0] or None
119
120 - def _filter_links(self, links,
121 text=None, text_regex=None,
122 url=None, url_regex=None,
123 predicate=None):
124 predicates = []
125 if text is not None:
126 predicates.append(lambda link: link.string == text)
127 if text_regex is not None:
128 predicates.append(lambda link: re_compile(text_regex).search(link.string or ''))
129 if url is not None:
130 predicates.append(lambda link: link.get('href') == url)
131 if url_regex is not None:
132 predicates.append(lambda link: re_compile(url_regex).search(link.get('href', '')))
133 if predicate:
134 predicate.append(predicate)
135
136 def f(link):
137 for p in predicates:
138 if not p(link):
139 return False
140 return True
141
142 return [link for link in links if f(link)]
143
152
167
169 """submits the currently selected form."""
170 if self.form is None:
171 raise BrowserError("No form selected.")
172 req = self.form.click(**kw)
173 return self.do_request(req)
174
176 return self.form[key]
177
179 self.form[key] = value
180
182 """Browser interface to test web.py apps.
183
184 b = AppBrowser(app)
185 b.open('/')
186 b.follow_link(text='Login')
187
188 b.select_form(name='login')
189 b['username'] = 'joe'
190 b['password'] = 'secret'
191 b.submit()
192
193 assert b.path == '/'
194 assert 'Welcome joe' in b.get_text()
195 """
199
202
204 """urllib2 handler to handle requests using web.py application."""
205 handler_order = 100
206
209
211 result = self.app.request(
212 localpart=req.get_selector(),
213 method=req.get_method(),
214 host=req.get_host(),
215 data=req.get_data(),
216 headers=dict(req.header_items()),
217 https=req.get_type() == "https"
218 )
219 return self._make_response(result, req.get_full_url())
220
223
224 try:
225 https_request = urllib2.HTTPHandler.do_request_
226 except AttributeError:
227
228 pass
229
231 data = "\r\n".join(["%s: %s" % (k, v) for k, v in result.header_items])
232 headers = httplib.HTTPMessage(StringIO(data))
233 response = urllib.addinfourl(StringIO(result.data), headers, url)
234 code, msg = result.status.split(None, 1)
235 response.code, response.msg = int(code), msg
236 return response
237