Repository: scrapy/scrapy
Pull request #1472 (2015-09-01 16:25:17)

[MRG+1] Incomplete submit button
If there is no `value` attribute, provides the empty string as described
in http://www.w3.org/TR/html5/forms.html#attr-button-value:

>The value attribute gives the element's value for the purposes
of form submission. The element's value is the value of the
element's value attribute, if there is one, or the empty string
otherwise.


Patch:
diff --git a/scrapy/http/request/form.py b/scrapy/http/request/form.py
index 1920cefe1..a12a2fd07 100644
--- a/scrapy/http/request/form.py
+++ b/scrapy/http/request/form.py
@@ -159,7 +159,7 @@ def _get_clickable(clickdata, form):
     # If we don't have clickdata, we just use the first clickable element
     if clickdata is None:
         el = clickables[0]
-        return (el.get('name'), el.get('value'))
+        return (el.get('name'), el.get('value') or '')
 
     # If clickdata is given, we compare it to the clickable elements to find a
     # match. We first look to see if the number is specified in clickdata,
@@ -171,7 +171,7 @@ def _get_clickable(clickdata, form):
         except IndexError:
             pass
         else:
-            return (el.get('name'), el.get('value'))
+            return (el.get('name'), el.get('value') or '')
 
     # We didn't find it, so now we build an XPath expression out of the other
     # arguments, because they can be used as such
@@ -179,7 +179,7 @@ def _get_clickable(clickdata, form):
             u''.join(u'[@%s="%s"]' % c for c in six.iteritems(clickdata))
     el = form.xpath(xpath)
     if len(el) == 1:
-        return (el[0].get('name'), el[0].get('value'))
+        return (el[0].get('name'), el[0].get('value') or '')
     elif len(el) > 1:
         raise ValueError("Multiple elements found (%r) matching the criteria "
                          "in clickdata: %r" % (el, clickdata))


Test patch:
diff --git a/tests/test_http_request.py b/tests/test_http_request.py
index b87045325..ff0941961 100644
--- a/tests/test_http_request.py
+++ b/tests/test_http_request.py
@@ -767,6 +767,40 @@ class FormRequestTest(RequestTest):
         self.assertEqual(fs[b'test2'], [b'val2'])
         self.assertEqual(fs[b'button1'], [b'submit1'])
 
+    def test_from_response_submit_novalue(self):
+        response = _buildresponse(
+            """<form action="post.php" method="POST">
+            <input type="hidden" name="test1" value="val1">
+            <input type="hidden" name="test2" value="val2">
+            <input type="submit" name="button1">Submit</button>
+            </form>""",
+            url="http://www.example.com/this/list.html")
+        req = self.request_class.from_response(response)
+        self.assertEqual(req.method, 'POST')
+        self.assertEqual(req.headers['Content-type'], b'application/x-www-form-urlencoded')
+        self.assertEqual(req.url, "http://www.example.com/this/post.php")
+        fs = _qs(req)
+        self.assertEqual(fs[b'test1'], [b'val1'])
+        self.assertEqual(fs[b'test2'], [b'val2'])
+        self.assertEqual(fs[b'button1'], [b''])
+
+    def test_from_response_button_novalue(self):
+        response = _buildresponse(
+            """<form action="post.php" method="POST">
+            <input type="hidden" name="test1" value="val1">
+            <input type="hidden" name="test2" value="val2">
+            <button type="submit" name="button1">Submit</button>
+            </form>""",
+            url="http://www.example.com/this/list.html")
+        req = self.request_class.from_response(response)
+        self.assertEqual(req.method, 'POST')
+        self.assertEqual(req.headers['Content-type'], b'application/x-www-form-urlencoded')
+        self.assertEqual(req.url, "http://www.example.com/this/post.php")
+        fs = _qs(req)
+        self.assertEqual(fs[b'test1'], [b'val1'])
+        self.assertEqual(fs[b'test2'], [b'val2'])
+        self.assertEqual(fs[b'button1'], [b''])
+
 def _buildresponse(body, **kwargs):
     kwargs.setdefault('body', body)
     kwargs.setdefault('url', 'http://example.com')