Parsing HTML Forms
==================

Sometimes in functional tests, information from a generated form must
be extracted in order to re-submit it as part of a subsequent request.
The `zope.testing.formparser` module can be used for this purpose.

The scanner is implemented using the `FormParser` class.  The
constructor arguments are the page data containing the form and
(optionally) the URL from which the page was retrieved:

  >>> import zope.testing.formparser

  >>> page_text = '''\
  ... <html><body>
  ...   <form name="form1" action="/cgi-bin/foobar.py" method="POST">
  ...     <input type="hidden" name="f1" value="today" />
  ...     <input type="submit" name="do-it-now" value="Go for it!" />
  ...     <input type="IMAGE" name="not-really" value="Don't."
  ...            src="dont.png" />
  ...     <select name="pick-two" size="3" multiple>
  ...       <option value="one" selected>First</option>
  ...       <option value="two" label="Second">Another</option>
  ...       <optgroup>
  ...         <option value="three">Third</option>
  ...         <option selected="selected">Fourth</option>
  ...       </optgroup>
  ...     </select>
  ...   </form>
  ...
  ...   Just for fun, a second form, after specifying a base:
  ...   <base href="http://www.example.com/base/" />
  ...   <form action = 'sproing/sprung.html' enctype="multipart/form">
  ...     <textarea name="sometext" rows="5">Some text.</textarea>
  ...     <input type="Image" name="action" value="Do something."
  ...            src="else.png" />
  ...     <input type="text" value="" name="multi" size="2" />
  ...     <input type="text" value="" name="multi" size="3" />
  ...   </form>
  ... </body></html>
  ... '''

  >>> parser = zope.testing.formparser.FormParser(page_text)
  >>> forms = parser.parse()

  >>> len(forms)
  2
  >>> forms.form1 is forms[0]
  True
  >>> forms.form1 is forms[1]
  False

More often, the `parse()` convenience function is all that's needed:

  >>> forms = zope.testing.formparser.parse(
  ...     page_text, "http://cgi.example.com/somewhere/form.html")

  >>> len(forms)
  2
  >>> forms.form1 is forms[0]
  True
  >>> forms.form1 is forms[1]
  False

Once we have the form we're interested in, we can check form
attributes and individual field values:

  >>> form = forms.form1
  >>> form.enctype
  'application/x-www-form-urlencoded'
  >>> form.method
  'post'

  >>> keys = form.keys()
  >>> keys.sort()
  >>> keys
  ['do-it-now', 'f1', 'not-really', 'pick-two']

  >>> not_really = form["not-really"]
  >>> not_really.type
  'image'
  >>> not_really.value
  "Don't."
  >>> not_really.readonly
  False
  >>> not_really.disabled
  False

Note that relative URLs are converted to absolute URLs based on the
``<base>`` element (if present) or using the base passed in to the
constructor.

  >>> form.action
  'http://cgi.example.com/cgi-bin/foobar.py'
  >>> not_really.src
  'http://cgi.example.com/somewhere/dont.png'

  >>> forms[1].action
  'http://www.example.com/base/sproing/sprung.html'
  >>> forms[1]["action"].src
  'http://www.example.com/base/else.png'

Fields which are repeated are reported as lists of objects that
represent each instance of the field::

  >>> field = forms[1]["multi"]
  >>> type(field)
  <type 'list'>
  >>> [o.value for o in field]
  ['', '']
  >>> [o.size for o in field]
  [2, 3]

The ``<textarea>`` element provides some additional attributes:

  >>> ta = forms[1]["sometext"]
  >>> print ta.rows
  5
  >>> print ta.cols
  None
  >>> ta.value
  'Some text.'

The ``<select>`` element provides access to the options as well:

  >>> select = form["pick-two"]
  >>> select.multiple
  True
  >>> select.size
  3
  >>> select.type
  'select'
  >>> select.value
  ['one', 'Fourth']

  >>> options = select.options
  >>> len(options)
  4
  >>> [opt.label for opt in options]
  ['First', 'Second', 'Third', 'Fourth']
  >>> [opt.value for opt in options]
  ['one', 'two', 'three', 'Fourth']
