From ee1f7847a437f81b8d6b7be1dce3d4a49b39701c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Fri, 13 Apr 2012 12:12:33 -0300 Subject: [PATCH] more lxml form fixes and test cases. #111 * Do not treat "coord" attribute specially, just pass "NN,NN" as clickdata value * Raise explicit ValueError if not clickable is found * Fix bug looking for clickeables trough xpath when there is more than one form * Test from_response with multiple clickdata --- scrapy/http/request/form.py | 17 ++++---- scrapy/tests/test_http_request.py | 64 +++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/scrapy/http/request/form.py b/scrapy/http/request/form.py index 896ce3448..e2a032490 100644 --- a/scrapy/http/request/form.py +++ b/scrapy/http/request/form.py @@ -118,17 +118,14 @@ def _get_clickable(clickdata, clickables, form): # We didn't find it, so now we build an XPath expression out of the other # arguments, because they can be used as such - xpath_pred = [] - for k, v in clickdata.items(): - if k == 'coord': - v = ','.join(str(c) for c in v) - xpath_pred.append('[@%s="%s"]' % (k, v)) - - xpath_expr = '//*%s' % ''.join(xpath_pred) - el = form.xpath(xpath_expr) - if len(el) > 1: + xpath = u'.//*' + \ + u''.join(u'[@%s="%s"]' % tuple(c) for c in clickdata.iteritems()) + el = form.xpath(xpath) + if len(el) == 1: + return (el[0].name, el[0].value) + elif len(el) > 1: raise MultipleElementsFound("Multiple elements found (%r) " "matching the criteria in clickdata: %r" % (el, clickdata)) else: - return (el[0].name, el[0].value) + raise ValueError('No clickeable element matching clickdata') diff --git a/scrapy/tests/test_http_request.py b/scrapy/tests/test_http_request.py index 091982742..05ec68378 100644 --- a/scrapy/tests/test_http_request.py +++ b/scrapy/tests/test_http_request.py @@ -306,6 +306,59 @@ class FormRequestTest(RequestTest): self.assertEqual(urlargs['one'], ['1']) self.assertEqual(urlargs['two'], ['2']) + def test_from_response_multiple_clickdata(self): + respbody = """ +
+ + + + +
+ """ + response = HtmlResponse("http://www.example.com/this/list.html", body=respbody) + r1 = self.request_class.from_response(response, \ + clickdata={'name': 'clickeable', 'value': 'clicked2'}) + urlargs = cgi.parse_qs(urlparse(r1.url).query) + self.assertEqual(urlargs['clickeable'], ['clicked2']) + self.assertEqual(urlargs['one'], ['clicked1']) + self.assertEqual(urlargs['two'], ['clicked2']) + + def test_from_response_unicode_clickdata(self): + body = u""" +
+ + + + +
+ """ + response = HtmlResponse("http://www.example.com", body=body, \ + encoding='utf-8') + r1 = self.request_class.from_response(response, \ + clickdata={'name': u'price in \u00a3'}) + urlargs = cgi.parse_qs(urlparse(r1.url).query) + self.assertTrue(urlargs[u'price in \u00a3'.encode('utf-8')]) + + def test_from_response_multiple_forms_clickdata(self): + body = u""" +
+ + +
+
+ + +
+ """ + res = HtmlResponse("http://example.com", body=body, encoding='utf-8') + req = self.request_class.from_response(res, \ + formname='form2', \ + clickdata={'name': 'clickeable'}) + urlargs = cgi.parse_qs(urlparse(req.url).query) + self.assertEqual(urlargs['clickeable'], ['clicked']) + self.assertEqual(urlargs['field2'], ['value2']) + self.assertFalse('field1' in urlargs, urlargs) + def test_from_response_dont_click(self): respbody = """
@@ -336,6 +389,17 @@ class FormRequestTest(RequestTest): response, clickdata={'type': 'submit'}) + def test_from_response_non_matching_clickdata(self): + body = """ + + +
+ """ + res = HtmlResponse("http://example.com", body=body) + self.assertRaises(ValueError, + self.request_class.from_response, res, + clickdata={'nonexistent': 'notme'}) + def test_from_response_errors_noform(self): respbody = """""" response = HtmlResponse("http://www.example.com/lala.html", body=respbody)