[Checkins] SVN: zc.testbrowser/trunk/src/zc/testbrowser/ Progress
point, so others can help with the work.
Stephan Richter
srichter at cosmos.phy.tufts.edu
Mon Sep 24 10:20:43 EDT 2007
Log message for revision 79878:
Progress point, so others can help with the work.
Changed:
U zc.testbrowser/trunk/src/zc/testbrowser/README.txt
U zc.testbrowser/trunk/src/zc/testbrowser/interfaces.py
U zc.testbrowser/trunk/src/zc/testbrowser/real.js
U zc.testbrowser/trunk/src/zc/testbrowser/real.py
-=-
Modified: zc.testbrowser/trunk/src/zc/testbrowser/README.txt
===================================================================
--- zc.testbrowser/trunk/src/zc/testbrowser/README.txt 2007-09-24 14:09:03 UTC (rev 79877)
+++ zc.testbrowser/trunk/src/zc/testbrowser/README.txt 2007-09-24 14:20:43 UTC (rev 79878)
@@ -25,189 +25,189 @@
True
-Page Contents
--------------
-
-The contents of the current page are available:
-
- >>> browser.contents
- '...<h1>Simple Page</h1>...'
-
-Note: Unfortunately, ellipsis (...) cannot be used at the beginning of the
-output (this is a limitation of doctest).
-
-Making assertions about page contents is easy.
-
- >>> '<h1>Simple Page</h1>' in browser.contents
- True
-
-
-Checking for HTML
------------------
-
-Not all URLs return HTML. Of course our simple page does:
-
- >>> browser.isHtml
- True
-
-But if we load an image (or other binary file), we do not get HTML:
-
- >>> browser.open('zope3logo.gif')
- >>> browser.isHtml
- False
-
-
-HTML Page Title
-----------------
-
-Another useful helper property is the title:
-
- >>> browser.open('index.html')
- >>> browser.title
- 'Simple Page'
-
-If a page does not provide a title, it is simply ``None``:
-
- >>> browser.open('notitle.html')
- >>> browser.title
-
-However, if the output is not HTML, then an error will occur trying to access
-the title:
-
- >>> browser.open('zope3logo.gif')
- >>> browser.title
- Traceback (most recent call last):
- ...
- BrowserStateError: not viewing HTML
-
-
-Navigation and Link Objects
----------------------------
-
-If you want to simulate clicking on a link, get the link and call its `click`
-method. In the `navigate.html` file there are several links set up to
-demonstrate the capabilities of the link objects and their `click` method.
-
-The simplest way to get a link is via the anchor text. In other words
-the text you would see in a browser:
-
- >>> browser.open('navigate.html')
- >>> browser.contents
- '...<a href="target.html">Link Text</a>...'
- >>> link = browser.getLink('Link Text')
- >>> link
- <Link text='Link Text' url='http://localhost:.../target.html'>
-
-Link objects comply with the ILink interface.
-
- >>> verifyObject(zc.testbrowser.interfaces.ILink, link)
- True
-
-Links expose several attributes for easy access.
-
- >>> link.text
- 'Link Text'
-
-Links can be "clicked" and the browser will navigate to the referenced URL.
-
- >>> link.click()
- >>> browser.url
- 'http://localhost:.../target.html'
- >>> browser.contents
- '...This page is the target of a link...'
-
-When finding a link by its text, whitespace is normalized.
-
- >>> browser.open('navigate.html')
- >>> browser.contents
- '...> Link Text \n with Whitespace\tNormalization (and parens) </...'
- >>> link = browser.getLink('Link Text with Whitespace Normalization '
- ... '(and parens)')
- >>> link
- <Link text='Link Text with Whitespace Normalization (and parens)'...>
- >>> link.text
- 'Link Text with Whitespace Normalization (and parens)'
- >>> link.click()
- >>> browser.url
- 'http://localhost:.../target.html'
-
-When a link text matches more than one link, by default the first one is
-chosen. You can, however, specify the index of the link and thus retrieve a
-later matching link:
-
- >>> browser.open('navigate.html')
- >>> browser.getLink('Link Text')
- <Link text='Link Text' ...>
-
- >>> browser.getLink('Link Text', index=1)
- <Link text='Link Text with Whitespace Normalization (and parens)' ...>
-
-Note that clicking a link object after its browser page has expired will
-generate an error.
-
- >>> link.click()
- Traceback (most recent call last):
- ...
- ExpiredError
-
-You can also find links by URL,
-
- >>> browser.open('navigate.html')
- >>> browser.getLink(url='target.html').click()
- >>> browser.url
- 'http://localhost:.../target.html'
-
-or its id:
-
- >>> browser.open('navigate.html')
- >>> browser.contents
- '...<a href="target.html" id="anchorid">By Anchor Id</a>...'
-
- >>> browser.getLink(id='anchorid').click()
- >>> browser.url
- 'http://localhost:.../target.html'
-
-You thought we were done here? Not so quickly. The `getLink` method also
-supports image maps, though not by specifying the coordinates, but using the
-area's id:
-
- >>> browser.open('navigate.html')
- >>> link = browser.getLink(id='zope3')
- >>> link.click()
- >>> browser.url
- 'http://localhost:.../target.html'
-
-Getting a nonexistent link raises an exception.
-
- >>> browser.open('navigate.html')
- >>> browser.getLink('This does not exist')
- Traceback (most recent call last):
- ...
- LinkNotFoundError
-
-
-Other Navigation
-----------------
-
-Like in any normal browser, you can reload a page:
-
- >>> browser.open('index.html')
- >>> browser.url
- 'http://localhost:.../index.html'
- >>> browser.reload()
- >>> browser.url
- 'http://localhost:.../index.html'
-
-You can also go back:
-
- >>> browser.open('notitle.html')
- >>> browser.url
- 'http://localhost:.../notitle.html'
- >>> browser.goBack()
- >>> browser.url
- 'http://localhost:.../index.html'
-
-
+#Page Contents
+#-------------
+#
+#The contents of the current page are available:
+#
+# >>> browser.contents
+# '...<h1>Simple Page</h1>...'
+#
+#Note: Unfortunately, ellipsis (...) cannot be used at the beginning of the
+#output (this is a limitation of doctest).
+#
+#Making assertions about page contents is easy.
+#
+# >>> '<h1>Simple Page</h1>' in browser.contents
+# True
+#
+#
+#Checking for HTML
+#-----------------
+#
+#Not all URLs return HTML. Of course our simple page does:
+#
+# >>> browser.isHtml
+# True
+#
+#But if we load an image (or other binary file), we do not get HTML:
+#
+# >>> browser.open('zope3logo.gif')
+# >>> browser.isHtml
+# False
+#
+#
+#HTML Page Title
+#----------------
+#
+#Another useful helper property is the title:
+#
+# >>> browser.open('index.html')
+# >>> browser.title
+# 'Simple Page'
+#
+#If a page does not provide a title, it is simply ``None``:
+#
+# >>> browser.open('notitle.html')
+# >>> browser.title
+#
+#However, if the output is not HTML, then an error will occur trying to access
+#the title:
+#
+# >>> browser.open('zope3logo.gif')
+# >>> browser.title
+# Traceback (most recent call last):
+# ...
+# BrowserStateError: not viewing HTML
+#
+#
+#Navigation and Link Objects
+#---------------------------
+#
+#If you want to simulate clicking on a link, get the link and call its `click`
+#method. In the `navigate.html` file there are several links set up to
+#demonstrate the capabilities of the link objects and their `click` method.
+#
+#The simplest way to get a link is via the anchor text. In other words
+#the text you would see in a browser:
+#
+# >>> browser.open('navigate.html')
+# >>> browser.contents
+# '...<a href="target.html">Link Text</a>...'
+# >>> link = browser.getLink('Link Text')
+# >>> link
+# <Link text='Link Text' url='http://localhost:.../target.html'>
+#
+#Link objects comply with the ILink interface.
+#
+# >>> verifyObject(zc.testbrowser.interfaces.ILink, link)
+# True
+#
+#Links expose several attributes for easy access.
+#
+# >>> link.text
+# 'Link Text'
+#
+#Links can be "clicked" and the browser will navigate to the referenced URL.
+#
+# >>> link.click()
+# >>> browser.url
+# 'http://localhost:.../target.html'
+# >>> browser.contents
+# '...This page is the target of a link...'
+#
+#When finding a link by its text, whitespace is normalized.
+#
+# >>> browser.open('navigate.html')
+# >>> browser.contents
+# '...> Link Text \n with Whitespace\tNormalization (and parens) </...'
+# >>> link = browser.getLink('Link Text with Whitespace Normalization '
+# ... '(and parens)')
+# >>> link
+# <Link text='Link Text with Whitespace Normalization (and parens)'...>
+# >>> link.text
+# 'Link Text with Whitespace Normalization (and parens)'
+# >>> link.click()
+# >>> browser.url
+# 'http://localhost:.../target.html'
+#
+#When a link text matches more than one link, by default the first one is
+#chosen. You can, however, specify the index of the link and thus retrieve a
+#later matching link:
+#
+# >>> browser.open('navigate.html')
+# >>> browser.getLink('Link Text')
+# <Link text='Link Text' ...>
+#
+# >>> browser.getLink('Link Text', index=1)
+# <Link text='Link Text with Whitespace Normalization (and parens)' ...>
+#
+#Note that clicking a link object after its browser page has expired will
+#generate an error.
+#
+# >>> link.click()
+# Traceback (most recent call last):
+# ...
+# ExpiredError
+#
+#You can also find links by URL,
+#
+# >>> browser.open('navigate.html')
+# >>> browser.getLink(url='target.html').click()
+# >>> browser.url
+# 'http://localhost:.../target.html'
+#
+#or its id:
+#
+# >>> browser.open('navigate.html')
+# >>> browser.contents
+# '...<a href="target.html" id="anchorid">By Anchor Id</a>...'
+#
+# >>> browser.getLink(id='anchorid').click()
+# >>> browser.url
+# 'http://localhost:.../target.html'
+#
+#You thought we were done here? Not so quickly. The `getLink` method also
+#supports image maps, though not by specifying the coordinates, but using the
+#area's id:
+#
+# >>> browser.open('navigate.html')
+# >>> link = browser.getLink(id='zope3')
+# >>> link.click()
+# >>> browser.url
+# 'http://localhost:.../target.html'
+#
+#Getting a nonexistent link raises an exception.
+#
+# >>> browser.open('navigate.html')
+# >>> browser.getLink('This does not exist')
+# Traceback (most recent call last):
+# ...
+# LinkNotFoundError
+#
+#
+#Other Navigation
+#----------------
+#
+#Like in any normal browser, you can reload a page:
+#
+# >>> browser.open('index.html')
+# >>> browser.url
+# 'http://localhost:.../index.html'
+# >>> browser.reload()
+# >>> browser.url
+# 'http://localhost:.../index.html'
+#
+#You can also go back:
+#
+# >>> browser.open('notitle.html')
+# >>> browser.url
+# 'http://localhost:.../notitle.html'
+# >>> browser.goBack()
+# >>> browser.url
+# 'http://localhost:.../index.html'
+#
+#
Controls
--------
@@ -217,238 +217,241 @@
>>> browser.open('controls.html')
-Obtaining a Control
-~~~~~~~~~~~~~~~~~~~
-
-You look up browser controls with the 'getControl' method. The default first
-argument is 'label', and looks up the form on the basis of any associated
-label.
-
- >>> control = browser.getControl('Text Control')
- >>> control
- <Control name='text-value' type='text'>
- >>> browser.getControl(label='Text Control') # equivalent
- <Control name='text-value' type='text'>
-
-If you request a control that doesn't exist, the code raises a LookupError:
-
- >>> browser.getControl('Does Not Exist')
- Traceback (most recent call last):
- ...
- LookupError: label 'Does Not Exist'
-
-If you request a control with an ambiguous lookup, the code raises an
-AmbiguityError.
-
- >>> browser.getControl('Ambiguous Control')
- Traceback (most recent call last):
- ...
- AmbiguityError: label 'Ambiguous Control'
-
-This is also true if an option in a control is ambiguous in relation to
-the control itself.
-
- >>> browser.getControl('Sub-control Ambiguity')
- Traceback (most recent call last):
- ...
- AmbiguityError: label 'Sub-control Ambiguity'
-
-Ambiguous controls may be specified using an index value. We use the control's
-value attribute to show the two controls; this attribute is properly introduced
-below.
-
- >>> browser.getControl('Ambiguous Control', index=0)
- <Control name='ambiguous-control-name' type='text'>
- >>> browser.getControl('Ambiguous Control', index=0).value
- 'First'
- >>> browser.getControl('Ambiguous Control', index=1).value
- 'Second'
- >>> browser.getControl('Sub-control Ambiguity', index=0)
- <ListControl name='ambiguous-subcontrol' type='select'>
- >>> browser.getControl('Sub-control Ambiguity', index=1).optionValue
- 'ambiguous'
-
-Label searches are against stripped, whitespace-normalized, no-tag versions of
-the text. Text applied to searches is also stripped and whitespace normalized.
-The search finds results if the text search finds the whole words of your
-text in a label. Thus, for instance, a search for 'Add' will match the label
-'Add a Client' but not 'Address'. Case is honored.
-
- >>> browser.getControl('Label Needs Whitespace Normalization')
- <Control name='label-needs-normalization' type='text'>
- >>> browser.getControl('label needs whitespace normalization')
- Traceback (most recent call last):
- ...
- LookupError: label 'label needs whitespace normalization'
- >>> browser.getControl(' Label Needs Whitespace ')
- <Control name='label-needs-normalization' type='text'>
- >>> browser.getControl('Whitespace')
- <Control name='label-needs-normalization' type='text'>
- >>> browser.getControl('hitespace')
- Traceback (most recent call last):
- ...
- LookupError: label 'hitespace'
- >>> browser.getControl('[non word characters should not confuse]')
- <Control name='non-word-characters' type='text'>
-
-Multiple labels can refer to the same control (simply because that is possible
-in the HTML 4.0 spec).
-
- >>> browser.getControl('Multiple labels really')
- <Control name='two-labels' type='text'>
- >>> browser.getControl('really are possible')
- <Control name='two-labels' type='text'>
- >>> browser.getControl('really') # OK: ambiguous labels, but not ambiguous control
- <Control name='two-labels' type='text'>
-
-A label can be connected with a control using the 'for' attribute and also by
-containing a control.
-
- >>> browser.getControl(
- ... 'Labels can be connected by containing their respective fields')
- <Control name='contained-in-label' type='text'>
-
-Get also accepts one other search argument, 'name'. Only one of 'label' and
-'name' may be used at a time. The 'name' keyword searches form field names.
-
- >>> browser.getControl(name='text-value')
- <Control name='text-value' type='text'>
- >>> browser.getControl(name='ambiguous-control-name')
- Traceback (most recent call last):
- ...
- AmbiguityError: name 'ambiguous-control-name'
- >>> browser.getControl(name='does-not-exist')
- Traceback (most recent call last):
- ...
- LookupError: name 'does-not-exist'
- >>> browser.getControl(name='ambiguous-control-name', index=1).value
- 'Second'
-
-Combining 'label' and 'name' raises a ValueError, as does supplying neither of
-them.
-
- >>> browser.getControl(label='Ambiguous Control', name='ambiguous-control-name')
- Traceback (most recent call last):
- ...
- ValueError: Supply one and only one of "label" and "name" as arguments
- >>> browser.getControl()
- Traceback (most recent call last):
- ...
- ValueError: Supply one and only one of "label" and "name" as arguments
-
-Radio and checkbox fields are unusual in that their labels and names may point
-to different objects: names point to logical collections of radio buttons or
-checkboxes, but labels may only be used for individual choices within the
-logical collection. This means that obtaining a radio button by label gets a
-different object than obtaining the radio collection by name. Select options
-may also be searched by label.
-
- >>> browser.getControl(name='radio-value')
- <ListControl name='radio-value' type='radio'>
- >>> browser.getControl('Zwei')
- <ItemControl name='radio-value' type='radio' optionValue='2' selected=True>
- >>> browser.getControl('One')
- <ItemControl name='multi-checkbox-value' type='checkbox' optionValue='1' selected=True>
- >>> browser.getControl('Tres')
- <ItemControl name='single-select-value' type='select' optionValue='3' selected=False>
-
-Characteristics of controls and subcontrols are discussed below.
-
-Control Objects
-~~~~~~~~~~~~~~~
-
-Controls provide IControl.
-
- >>> ctrl = browser.getControl('Text Control')
- >>> ctrl
- <Control name='text-value' type='text'>
- >>> verifyObject(zc.testbrowser.interfaces.IControl, ctrl)
- True
-
-They have several useful attributes:
-
- - the name as which the control is known to the form:
-
- >>> ctrl.name
- 'text-value'
-
- - the value of the control, which may also be set:
-
- >>> ctrl.value
- 'Some Text'
- >>> ctrl.value = 'More Text'
- >>> ctrl.value
- 'More Text'
-
- - the type of the control:
-
- >>> ctrl.type
- 'text'
-
- - a flag describing whether the control is disabled:
-
- >>> ctrl.disabled
- False
-
- - and a flag to tell us whether the control can have multiple values:
-
- >>> ctrl.multiple
- False
-
-Additionally, controllers for select, radio, and checkbox provide IListControl.
-These fields have four other attributes and an additional method:
-
+#Obtaining a Control
+#~~~~~~~~~~~~~~~~~~~
+#
+#You look up browser controls with the 'getControl' method. The default first
+#argument is 'label', and looks up the form on the basis of any associated
+#label.
+#
+# >>> control = browser.getControl('Text Control')
+# >>> control
+# <Control name='text-value' type='text'>
+# >>> browser.getControl(label='Text Control') # equivalent
+# <Control name='text-value' type='text'>
+#
+#If you request a control that doesn't exist, the code raises a LookupError:
+#
+# >>> browser.getControl('Does Not Exist')
+# Traceback (most recent call last):
+# ...
+# LookupError: label 'Does Not Exist'
+#
+#If you request a control with an ambiguous lookup, the code raises an
+#AmbiguityError.
+#
+# >>> browser.getControl('Ambiguous Control')
+# Traceback (most recent call last):
+# ...
+# AmbiguityError: label 'Ambiguous Control'
+#
+#This is also true if an option in a control is ambiguous in relation to
+#the control itself.
+#
+# >>> browser.getControl('Sub-control Ambiguity')
+# Traceback (most recent call last):
+# ...
+# AmbiguityError: label 'Sub-control Ambiguity'
+#
+#Ambiguous controls may be specified using an index value. We use the control's
+#value attribute to show the two controls; this attribute is properly introduced
+#below.
+#
+# >>> browser.getControl('Ambiguous Control', index=0)
+# <Control name='ambiguous-control-name' type='text'>
+# >>> browser.getControl('Ambiguous Control', index=0).value
+# 'First'
+# >>> browser.getControl('Ambiguous Control', index=1).value
+# 'Second'
+# >>> browser.getControl('Sub-control Ambiguity', index=0)
+# <ListControl name='ambiguous-subcontrol' type='select'>
+# >>> browser.getControl('Sub-control Ambiguity', index=1).optionValue
+# 'ambiguous'
+#
+#Label searches are against stripped, whitespace-normalized, no-tag versions of
+#the text. Text applied to searches is also stripped and whitespace normalized.
+#The search finds results if the text search finds the whole words of your
+#text in a label. Thus, for instance, a search for 'Add' will match the label
+#'Add a Client' but not 'Address'. Case is honored.
+#
+# >>> browser.getControl('Label Needs Whitespace Normalization')
+# <Control name='label-needs-normalization' type='text'>
+# >>> browser.getControl('label needs whitespace normalization')
+# Traceback (most recent call last):
+# ...
+# LookupError: label 'label needs whitespace normalization'
+# >>> browser.getControl(' Label Needs Whitespace ')
+# <Control name='label-needs-normalization' type='text'>
+# >>> browser.getControl('Whitespace')
+# <Control name='label-needs-normalization' type='text'>
+# >>> browser.getControl('hitespace')
+# Traceback (most recent call last):
+# ...
+# LookupError: label 'hitespace'
+# >>> browser.getControl('[non word characters should not confuse]')
+# <Control name='non-word-characters' type='text'>
+#
+#Multiple labels can refer to the same control (simply because that is possible
+#in the HTML 4.0 spec).
+#
+# >>> browser.getControl('Multiple labels really')
+# <Control name='two-labels' type='text'>
+#
+# >>> browser.getControl('really are possible')
+# <Control name='two-labels' type='text'>
+#
+# >>> browser.getControl('really') # OK: ambiguous labels, but not ambiguous control
+# <Control name='two-labels' type='text'>
+#
+#A label can be connected with a control using the 'for' attribute and also by
+#containing a control.
+#
+# >>> browser.getControl(
+# ... 'Labels can be connected by containing their respective fields')
+# <Control name='contained-in-label' type='text'>
+#
+#Get also accepts one other search argument, 'name'. Only one of 'label' and
+#'name' may be used at a time. The 'name' keyword searches form field names.
+#
+# >>> browser.getControl(name='text-value')
+# <Control name='text-value' type='text'>
+# >>> browser.getControl(name='ambiguous-control-name')
+# Traceback (most recent call last):
+# ...
+# AmbiguityError: name 'ambiguous-control-name'
+# >>> browser.getControl(name='does-not-exist')
+# Traceback (most recent call last):
+# ...
+# LookupError: name 'does-not-exist'
+# >>> browser.getControl(name='ambiguous-control-name', index=1).value
+# 'Second'
+#
+#Combining 'label' and 'name' raises a ValueError, as does supplying neither of
+#them.
+#
+# >>> browser.getControl(label='Ambiguous Control', name='ambiguous-control-name')
+# Traceback (most recent call last):
+# ...
+# ValueError: Supply one and only one of "label" and "name" as arguments
+# >>> browser.getControl()
+# Traceback (most recent call last):
+# ...
+# ValueError: Supply one and only one of "label" and "name" as arguments
+#
+#Radio and checkbox fields are unusual in that their labels and names may point
+#to different objects: names point to logical collections of radio buttons or
+#checkboxes, but labels may only be used for individual choices within the
+#logical collection. This means that obtaining a radio button by label gets a
+#different object than obtaining the radio collection by name. Select options
+#may also be searched by label.
+#
+# >>> browser.getControl(name='radio-value')
+# <ListControl name='radio-value' type='radio'>
+# >>> browser.getControl('Zwei')
+# <ItemControl name='radio-value' type='radio' optionValue='2' selected=True>
+# >>> browser.getControl('One')
+# <ItemControl name='multi-checkbox-value' type='checkbox' optionValue='1' selected=True>
+# >>> browser.getControl('Tres')
+# <ItemControl name='single-select-value' type='select' optionValue='3' selected=False>
+#
+#Characteristics of controls and subcontrols are discussed below.
+#
+#Control Objects
+#~~~~~~~~~~~~~~~
+#
+#Controls provide IControl.
+#
+# >>> ctrl = browser.getControl('Text Control')
+# >>> ctrl
+# <Control name='text-value' type='text'>
+# >>> verifyObject(zc.testbrowser.interfaces.IControl, ctrl)
+# True
+#
+#They have several useful attributes:
+#
+# - the name as which the control is known to the form:
+#
+# >>> ctrl.name
+# 'text-value'
+#
+# - the value of the control, which may also be set:
+#
+# >>> ctrl.value
+# 'Some Text'
+# >>> ctrl.value = 'More Text'
+# >>> ctrl.value
+# 'More Text'
+#
+# - the type of the control:
+#
+# >>> ctrl.type
+# 'text'
+#
+# - a flag describing whether the control is disabled:
+#
+# >>> ctrl.disabled
+# False
+#
+# - and a flag to tell us whether the control can have multiple values:
+#
+# >>> ctrl.multiple
+# False
+#
+#Additionally, controllers for select, radio, and checkbox provide IListControl.
+#These fields have four other attributes and an additional method:
+#
>>> ctrl = browser.getControl('Multiple Select Control')
- >>> ctrl
- <ListControl name='multi-select-value' type='select'>
- >>> ctrl.disabled
- False
- >>> ctrl.multiple
- True
- >>> verifyObject(zc.testbrowser.interfaces.IListControl, ctrl)
- True
- - 'options' lists all available value options.
-
- >>> ctrl.options
- ['1', '2', '3']
-
- - 'displayOptions' lists all available options by label. The 'label'
- attribute on an option has precedence over its contents, which is why
- our last option is 'Third' in the display.
-
- >>> ctrl.displayOptions
- ['Un', 'Deux', 'Third']
-
- - 'displayValue' lets you get and set the displayed values of the control
- of the select box, rather than the actual values.
-
- >>> ctrl.value
- []
- >>> ctrl.displayValue
- []
- >>> ctrl.displayValue = ['Un', 'Deux']
- >>> ctrl.displayValue
- ['Un', 'Deux']
- >>> ctrl.value
- ['1', '2']
-
- - 'controls' gives you a list of the subcontrol objects in the control
- (subcontrols are discussed below).
-
- >>> ctrl.controls
- [<ItemControl name='multi-select-value' type='select' optionValue='1' selected=True>,
- <ItemControl name='multi-select-value' type='select' optionValue='2' selected=True>,
- <ItemControl name='multi-select-value' type='select' optionValue='3' selected=False>]
-
- - The 'getControl' method lets you get subcontrols by their label or their value.
-
- >>> ctrl.getControl('Un')
- <ItemControl name='multi-select-value' type='select' optionValue='1' selected=True>
- >>> ctrl.getControl('Deux')
- <ItemControl name='multi-select-value' type='select' optionValue='2' selected=True>
- >>> ctrl.getControl('Trois') # label attribute
- <ItemControl name='multi-select-value' type='select' optionValue='3' selected=False>
+# >>> ctrl
+# <ListControl name='multi-select-value' type='select'>
+# >>> ctrl.disabled
+# False
+# >>> ctrl.multiple
+# True
+# >>> verifyObject(zc.testbrowser.interfaces.IListControl, ctrl)
+# True
+#
+# - 'options' lists all available value options.
+#
+# >>> ctrl.options
+# ['1', '2', '3']
+#
+# - 'displayOptions' lists all available options by label. The 'label'
+# attribute on an option has precedence over its contents, which is why
+# our last option is 'Third' in the display.
+#
+# >>> ctrl.displayOptions
+# ['Un', 'Deux', 'Third']
+#
+# - 'displayValue' lets you get and set the displayed values of the control
+# of the select box, rather than the actual values.
+#
+# >>> ctrl.value
+# []
+# >>> ctrl.displayValue
+# []
+# >>> ctrl.displayValue = ['Un', 'Deux']
+# >>> ctrl.displayValue
+# ['Un', 'Deux']
+# >>> ctrl.value
+# ['1', '2']
+#
+# - 'controls' gives you a list of the subcontrol objects in the control
+# (subcontrols are discussed below).
+#
+# >>> ctrl.controls
+# [<ItemControl name='multi-select-value' type='select' optionValue='1' selected=True>,
+# <ItemControl name='multi-select-value' type='select' optionValue='2' selected=True>,
+# <ItemControl name='multi-select-value' type='select' optionValue='3' selected=False>]
+#
+# - The 'getControl' method lets you get subcontrols by their label or their value.
+#
+# >>> ctrl.getControl('Un')
+# <ItemControl name='multi-select-value' type='select' optionValue='1' selected=True>
+# >>> ctrl.getControl('Deux')
+# <ItemControl name='multi-select-value' type='select' optionValue='2' selected=True>
+# >>> ctrl.getControl('Trois') # label attribute
+# <ItemControl name='multi-select-value' type='select' optionValue='3' selected=False>
>>> ctrl.getControl('Third') # contents
<ItemControl name='multi-select-value' type='select' optionValue='3' selected=False>
>>> browser.getControl('Third') # ambiguous in the browser, so useful
@@ -462,607 +465,607 @@
argument, which is a tuple of (x, y). These submit the forms, and are
demonstrated below as we examine each control individually.
-ItemControl Objects
-~~~~~~~~~~~~~~~~~~~
-
-As introduced briefly above, using labels to obtain elements of a logical
-radio button or checkbox collection returns item controls, which are parents.
-Manipulating the value of these controls affects the parent control.
-
- >>> browser.getControl(name='radio-value').value
- ['2']
- >>> browser.getControl('Zwei').optionValue # read-only.
- '2'
- >>> browser.getControl('Zwei').selected
- True
- >>> verifyObject(zc.testbrowser.interfaces.IItemControl,
- ... browser.getControl('Zwei'))
- True
- >>> browser.getControl('Ein').selected = True
- >>> browser.getControl('Ein').selected
- True
- >>> browser.getControl('Zwei').selected
- False
- >>> browser.getControl(name='radio-value').value
- ['1']
- >>> browser.getControl('Ein').selected = False
- >>> browser.getControl(name='radio-value').value
- []
- >>> browser.getControl('Zwei').selected = True
-
-Checkbox collections behave similarly, as shown below.
-
-Controls with subcontrols--
-
-Various Controls
-~~~~~~~~~~~~~~~~
-
-The various types of controls are demonstrated here.
-
- - Text Control
-
- The text control we already introduced above.
-
- - Password Control
-
- >>> ctrl = browser.getControl('Password Control')
- >>> ctrl
- <Control name='password-value' type='password'>
- >>> verifyObject(zc.testbrowser.interfaces.IControl, ctrl)
- True
- >>> ctrl.value
- 'Password'
- >>> ctrl.value = 'pass now'
- >>> ctrl.value
- 'pass now'
- >>> ctrl.disabled
- False
- >>> ctrl.multiple
- False
-
- - Hidden Control
-
- >>> ctrl = browser.getControl(name='hidden-value')
- >>> ctrl
- <Control name='hidden-value' type='hidden'>
- >>> verifyObject(zc.testbrowser.interfaces.IControl, ctrl)
- True
- >>> ctrl.value
- 'Hidden'
- >>> ctrl.value = 'More Hidden'
- >>> ctrl.disabled
- False
- >>> ctrl.multiple
- False
-
- - Text Area Control
-
- >>> ctrl = browser.getControl('Text Area Control')
- >>> ctrl
- <Control name='textarea-value' type='textarea'>
- >>> verifyObject(zc.testbrowser.interfaces.IControl, ctrl)
- True
- >>> ctrl.value
- ' Text inside\n area!\n '
- >>> ctrl.value = 'A lot of\n text.'
- >>> ctrl.disabled
- False
- >>> ctrl.multiple
- False
-
- - File Control
-
- File controls are used when a form has a file-upload field.
- To specify data, call the add_file method, passing:
-
- - A file-like object
-
- - a content type, and
-
- - a file name
-
- >>> ctrl = browser.getControl('File Control')
- >>> ctrl
- <Control name='file-value' type='file'>
- >>> verifyObject(zc.testbrowser.interfaces.IControl, ctrl)
- True
- >>> ctrl.value is None
- True
- >>> import cStringIO
-
- >>> ctrl.add_file(cStringIO.StringIO('File contents'),
- ... 'text/plain', 'test.txt')
-
- The file control (like the other controls) also knows if it is disabled
- or if it can have multiple values.
-
- >>> ctrl.disabled
- False
- >>> ctrl.multiple
- False
-
- - Selection Control (Single-Valued)
-
- >>> ctrl = browser.getControl('Single Select Control')
- >>> ctrl
- <ListControl name='single-select-value' type='select'>
- >>> verifyObject(zc.testbrowser.interfaces.IListControl, ctrl)
- True
- >>> ctrl.value
- ['1']
- >>> ctrl.value = ['2']
- >>> ctrl.disabled
- False
- >>> ctrl.multiple
- False
- >>> ctrl.options
- ['1', '2', '3']
- >>> ctrl.displayOptions
- ['Uno', 'Dos', 'Third']
- >>> ctrl.displayValue
- ['Dos']
- >>> ctrl.displayValue = ['Tres']
- >>> ctrl.displayValue
- ['Third']
- >>> ctrl.displayValue = ['Dos']
- >>> ctrl.displayValue
- ['Dos']
- >>> ctrl.displayValue = ['Third']
- >>> ctrl.displayValue
- ['Third']
- >>> ctrl.value
- ['3']
-
- - Selection Control (Multi-Valued)
-
- This was already demonstrated in the introduction to control objects above.
-
- - Checkbox Control (Single-Valued; Unvalued)
-
- >>> ctrl = browser.getControl(name='single-unvalued-checkbox-value')
- >>> ctrl
- <ListControl name='single-unvalued-checkbox-value' type='checkbox'>
- >>> verifyObject(zc.testbrowser.interfaces.IListControl, ctrl)
- True
- >>> ctrl.value
- True
- >>> ctrl.value = False
- >>> ctrl.disabled
- False
- >>> ctrl.multiple
- True
- >>> ctrl.options
- [True]
- >>> ctrl.displayOptions
- ['Single Unvalued Checkbox']
- >>> ctrl.displayValue
- []
- >>> verifyObject(
- ... zc.testbrowser.interfaces.IItemControl,
- ... browser.getControl('Single Unvalued Checkbox'))
- True
- >>> browser.getControl('Single Unvalued Checkbox').optionValue
- 'on'
- >>> browser.getControl('Single Unvalued Checkbox').selected
- False
- >>> ctrl.displayValue = ['Single Unvalued Checkbox']
- >>> ctrl.displayValue
- ['Single Unvalued Checkbox']
- >>> browser.getControl('Single Unvalued Checkbox').selected
- True
- >>> browser.getControl('Single Unvalued Checkbox').selected = False
- >>> browser.getControl('Single Unvalued Checkbox').selected
- False
- >>> ctrl.displayValue
- []
- >>> browser.getControl(
- ... name='single-disabled-unvalued-checkbox-value').disabled
- True
-
- - Checkbox Control (Single-Valued, Valued)
-
- >>> ctrl = browser.getControl(name='single-valued-checkbox-value')
- >>> ctrl
- <ListControl name='single-valued-checkbox-value' type='checkbox'>
- >>> verifyObject(zc.testbrowser.interfaces.IListControl, ctrl)
- True
- >>> ctrl.value
- ['1']
- >>> ctrl.value = []
- >>> ctrl.disabled
- False
- >>> ctrl.multiple
- True
- >>> ctrl.options
- ['1']
- >>> ctrl.displayOptions
- ['Single Valued Checkbox']
- >>> ctrl.displayValue
- []
- >>> verifyObject(
- ... zc.testbrowser.interfaces.IItemControl,
- ... browser.getControl('Single Valued Checkbox'))
- True
- >>> browser.getControl('Single Valued Checkbox').selected
- False
- >>> browser.getControl('Single Valued Checkbox').optionValue
- '1'
- >>> ctrl.displayValue = ['Single Valued Checkbox']
- >>> ctrl.displayValue
- ['Single Valued Checkbox']
- >>> browser.getControl('Single Valued Checkbox').selected
- True
- >>> browser.getControl('Single Valued Checkbox').selected = False
- >>> browser.getControl('Single Valued Checkbox').selected
- False
- >>> ctrl.displayValue
- []
-
- - Checkbox Control (Multi-Valued)
-
- >>> ctrl = browser.getControl(name='multi-checkbox-value')
- >>> ctrl
- <ListControl name='multi-checkbox-value' type='checkbox'>
- >>> verifyObject(zc.testbrowser.interfaces.IListControl, ctrl)
- True
- >>> ctrl.value
- ['1', '3']
- >>> ctrl.value = ['1', '2']
- >>> ctrl.disabled
- False
- >>> ctrl.multiple
- True
- >>> ctrl.options
- ['1', '2', '3']
- >>> ctrl.displayOptions
- ['One', 'Two', 'Three']
- >>> ctrl.displayValue
- ['One', 'Two']
- >>> ctrl.displayValue = ['Two']
- >>> ctrl.value
- ['2']
- >>> browser.getControl('Two').optionValue
- '2'
- >>> browser.getControl('Two').selected
- True
- >>> verifyObject(zc.testbrowser.interfaces.IItemControl,
- ... browser.getControl('Two'))
- True
- >>> browser.getControl('Three').selected = True
- >>> browser.getControl('Three').selected
- True
- >>> browser.getControl('Two').selected
- True
- >>> ctrl.value
- ['2', '3']
- >>> browser.getControl('Two').selected = False
- >>> ctrl.value
- ['3']
- >>> browser.getControl('Three').selected = False
- >>> ctrl.value
- []
-
- - Radio Control
-
- This is how you get a radio button based control:
-
- >>> ctrl = browser.getControl(name='radio-value')
-
- This shows the existing value of the control, as it was in the
- HTML received from the server:
-
- >>> ctrl.value
- ['2']
-
- We can then unselect it:
-
- >>> ctrl.value = []
- >>> ctrl.value
- []
-
- We can also reselect it:
-
- >>> ctrl.value = ['2']
- >>> ctrl.value
- ['2']
-
- displayValue shows the text the user would see next to the
- control:
-
- >>> ctrl.displayValue
- ['Zwei']
-
- This is just unit testing:
-
- >>> ctrl
- <ListControl name='radio-value' type='radio'>
- >>> verifyObject(zc.testbrowser.interfaces.IListControl, ctrl)
- True
- >>> ctrl.disabled
- False
- >>> ctrl.multiple
- False
- >>> ctrl.options
- ['1', '2', '3']
- >>> ctrl.displayOptions
- ['Ein', 'Zwei', 'Drei']
- >>> ctrl.displayValue = ['Ein']
- >>> ctrl.value
- ['1']
- >>> ctrl.displayValue
- ['Ein']
-
- The radio control subcontrols were illustrated above.
-
- - Image Control
-
- >>> ctrl = browser.getControl(name='image-value')
- >>> ctrl
- <ImageControl name='image-value' type='image'>
- >>> verifyObject(zc.testbrowser.interfaces.IImageSubmitControl, ctrl)
- True
- >>> ctrl.value
- ''
- >>> ctrl.disabled
- False
- >>> ctrl.multiple
- False
-
- - Submit Control
-
- >>> ctrl = browser.getControl(name='submit-value')
- >>> ctrl
- <SubmitControl name='submit-value' type='submit'>
- >>> browser.getControl('Submit This') # value of submit button is a label
- <SubmitControl name='submit-value' type='submit'>
- >>> browser.getControl('Standard Submit Control') # label tag is legal
- <SubmitControl name='submit-value' type='submit'>
- >>> browser.getControl('Submit') # multiple labels, but same control
- <SubmitControl name='submit-value' type='submit'>
- >>> verifyObject(zc.testbrowser.interfaces.ISubmitControl, ctrl)
- True
- >>> ctrl.value
- 'Submit This'
- >>> ctrl.disabled
- False
- >>> ctrl.multiple
- False
-
-Using Submitting Controls
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Both the submit and image type should be clickable and submit the form:
-
- >>> browser.getControl('Text Control').value = 'Other Text'
- >>> browser.getControl('Submit').click()
- >>> browser.contents
- "...'text-value': ['Other Text']..."
-
-Note that if you click a submit object after the associated page has expired,
-you will get an error.
-
- >>> browser.open('controls.html')
- >>> ctrl = browser.getControl('Submit')
- >>> ctrl.click()
- >>> ctrl.click()
- Traceback (most recent call last):
- ...
- ExpiredError
-
-All the above also holds true for the image control:
-
- >>> browser.open('controls.html')
- >>> browser.getControl('Text Control').value = 'Other Text'
- >>> browser.getControl(name='image-value').click()
- >>> browser.contents
- "...'text-value': ['Other Text']..."
-
- >>> browser.open('controls.html')
- >>> ctrl = browser.getControl(name='image-value')
- >>> ctrl.click()
- >>> ctrl.click()
- Traceback (most recent call last):
- ...
- ExpiredError
-
-But when sending an image, you can also specify the coordinate you clicked:
-
- >>> browser.open('controls.html')
- >>> browser.getControl(name='image-value').click((50,25))
- >>> browser.contents
- "...'image-value.x': ['50']...'image-value.y': ['25']..."
-
-Forms
------
-
-Because pages can have multiple forms with like-named controls, it is sometimes
-necessary to access forms by name or id. The browser's `forms` attribute can
-be used to do so. The key value is the form's name or id. If more than one
-form has the same name or id, the first one will be returned.
-
- >>> browser.open('forms.html')
- >>> form = browser.getForm(name='one')
-
-Form instances conform to the IForm interface.
-
- >>> verifyObject(zc.testbrowser.interfaces.IForm, form)
- True
-
-The form exposes several attributes related to forms:
-
- - The name of the form:
-
- >>> form.name
- 'one'
-
- - The id of the form:
-
- >>> form.id
- '1'
-
- - The action (target URL) when the form is submitted:
-
- >>> form.action
- 'http://localhost:.../forms.html'
-
- - The method (HTTP verb) used to transmit the form data:
-
- >>> form.method
- 'POST'
-
- - The encoding type of the form data:
-
- >>> form.enctype
- 'application/x-www-form-urlencoded'
-
-Besides those attributes, you have also a couple of methods. Like for the
-browser, you can get control objects, but limited to the current form...
-
- >>> form.getControl(name='text-value')
- <Control name='text-value' type='text'>
-
-...and submit the form.
-
- >>> form.submit('Submit')
- >>> browser.contents
- "...'text-value': ['First Text']..."
-
-Submitting also works without specifying a control, as shown below, which is
-it's primary reason for existing in competition with the control submission
-discussed above.
-
-Now let me show you briefly that looking up forms is sometimes important. In
-the `forms.html` template, we have four forms all having a text control named
-`text-value`. Now, if I use the browser's `get` method,
-
- >>> browser.open('forms.html')
- >>> browser.getControl(name='text-value')
- Traceback (most recent call last):
- ...
- AmbiguityError: name 'text-value'
- >>> browser.getControl('Text Control')
- Traceback (most recent call last):
- ...
- AmbiguityError: label 'Text Control'
-
-I'll always get an ambiguous form field. I can use the index argument, or
-with the `getForm` method I can disambiguate by searching only within a given
-form:
-
- >>> form = browser.getForm('2')
- >>> form.getControl(name='text-value').value
- 'Second Text'
- >>> form.submit('Submit')
- >>> browser.contents
- "...'text-value': ['Second Text']..."
- >>> browser.open('forms.html')
- >>> form = browser.getForm('2')
- >>> form.getControl('Submit').click()
- >>> browser.contents
- "...'text-value': ['Second Text']..."
- >>> browser.open('forms.html')
- >>> browser.getForm('3').getControl('Text Control').value
- 'Third Text'
-
-The last form on the page does not have a name, an id, or a submit button.
-Working with it is still easy, thanks to a index attribute that guarantees
-order. (Forms without submit buttons are sometimes useful for JavaScript.)
-
- >>> form = browser.getForm(index=3)
- >>> form.submit()
- >>> browser.contents
- "...'text-value': ['Fourth Text']..."
-
-If a form is requested that does not exists, an exception will be raised.
-
- >>> browser.open('forms.html')
- >>> form = browser.getForm('does-not-exist')
- Traceback (most recent call last):
- LookupError
-
-If the HTML page contains only one form, no arguments to `getForm` are
-needed:
-
- >>> browser.open('oneform.html')
- >>> browser.getForm()
- <zc.testbrowser...Form object at ...>
-
-If the HTML page contains more than one form, `index` is needed to
-disambiguate if no other arguments are provided:
-
- >>> browser.open('forms.html')
- >>> browser.getForm()
- Traceback (most recent call last):
- ValueError: if no other arguments are given, index is required.
-
-
-Performance Testing
--------------------
-
-Browser objects keep up with how much time each request takes. This can be
-used to ensure a particular request's performance is within a tolerable range.
-Be very careful using raw seconds, cross-machine differences can be huge,
-pystones is usually a better choice.
-
- >>> browser.open('index.html')
- >>> browser.lastRequestSeconds < 10 # really big number for safety
- True
- >>> browser.lastRequestPystones < 10000 # really big number for safety
- True
-
-
-Hand-Holding
-------------
-
-Instances of the various objects ensure that users don't set incorrect
-instance attributes accidentally.
-
- >>> browser.nonexistant = None
- Traceback (most recent call last):
- ...
- AttributeError: 'Browser' object has no attribute 'nonexistant'
-
- >>> form.nonexistant = None
- Traceback (most recent call last):
- ...
- AttributeError: 'Form' object has no attribute 'nonexistant'
-
- >>> control.nonexistant = None
- Traceback (most recent call last):
- ...
- AttributeError: 'Control' object has no attribute 'nonexistant'
-
- >>> link.nonexistant = None
- Traceback (most recent call last):
- ...
- AttributeError: 'Link' object has no attribute 'nonexistant'
-
-
-Fixed Bugs
-----------
-
-This section includes tests for bugs that were found and then fixed that don't
-fit into the more documentation-centric sections above.
-
-Spaces in URL
-~~~~~~~~~~~~~
-
-When URLs have spaces in them, they're handled correctly (before the bug was
-fixed, you'd get "ValueError: too many values to unpack"):
-
- >>> browser.open('navigate.html')
- >>> browser.getLink('Spaces in the URL').click()
-
-.goBack() Truncation
-~~~~~~~~~~~~~~~~~~~~
-
-The .goBack() method used to truncate the .contents.
-
- >>> browser.open('navigate.html')
- >>> actual_length = len(browser.contents)
-
- >>> browser.open('navigate.html')
- >>> browser.open('index.html')
- >>> browser.goBack()
- >>> len(browser.contents) == actual_length
- True
+#ItemControl Objects
+#~~~~~~~~~~~~~~~~~~~
+#
+#As introduced briefly above, using labels to obtain elements of a logical
+#radio button or checkbox collection returns item controls, which are parents.
+#Manipulating the value of these controls affects the parent control.
+#
+# >>> browser.getControl(name='radio-value').value
+# ['2']
+# >>> browser.getControl('Zwei').optionValue # read-only.
+# '2'
+# >>> browser.getControl('Zwei').selected
+# True
+# >>> verifyObject(zc.testbrowser.interfaces.IItemControl,
+# ... browser.getControl('Zwei'))
+# True
+# >>> browser.getControl('Ein').selected = True
+# >>> browser.getControl('Ein').selected
+# True
+# >>> browser.getControl('Zwei').selected
+# False
+# >>> browser.getControl(name='radio-value').value
+# ['1']
+# >>> browser.getControl('Ein').selected = False
+# >>> browser.getControl(name='radio-value').value
+# []
+# >>> browser.getControl('Zwei').selected = True
+#
+#Checkbox collections behave similarly, as shown below.
+#
+#Controls with subcontrols--
+#
+#Various Controls
+#~~~~~~~~~~~~~~~~
+#
+#The various types of controls are demonstrated here.
+#
+# - Text Control
+#
+# The text control we already introduced above.
+#
+# - Password Control
+#
+# >>> ctrl = browser.getControl('Password Control')
+# >>> ctrl
+# <Control name='password-value' type='password'>
+# >>> verifyObject(zc.testbrowser.interfaces.IControl, ctrl)
+# True
+# >>> ctrl.value
+# 'Password'
+# >>> ctrl.value = 'pass now'
+# >>> ctrl.value
+# 'pass now'
+# >>> ctrl.disabled
+# False
+# >>> ctrl.multiple
+# False
+#
+# - Hidden Control
+#
+# >>> ctrl = browser.getControl(name='hidden-value')
+# >>> ctrl
+# <Control name='hidden-value' type='hidden'>
+# >>> verifyObject(zc.testbrowser.interfaces.IControl, ctrl)
+# True
+# >>> ctrl.value
+# 'Hidden'
+# >>> ctrl.value = 'More Hidden'
+# >>> ctrl.disabled
+# False
+# >>> ctrl.multiple
+# False
+#
+# - Text Area Control
+#
+# >>> ctrl = browser.getControl('Text Area Control')
+# >>> ctrl
+# <Control name='textarea-value' type='textarea'>
+# >>> verifyObject(zc.testbrowser.interfaces.IControl, ctrl)
+# True
+# >>> ctrl.value
+# ' Text inside\n area!\n '
+# >>> ctrl.value = 'A lot of\n text.'
+# >>> ctrl.disabled
+# False
+# >>> ctrl.multiple
+# False
+#
+# - File Control
+#
+# File controls are used when a form has a file-upload field.
+# To specify data, call the add_file method, passing:
+#
+# - A file-like object
+#
+# - a content type, and
+#
+# - a file name
+#
+# >>> ctrl = browser.getControl('File Control')
+# >>> ctrl
+# <Control name='file-value' type='file'>
+# >>> verifyObject(zc.testbrowser.interfaces.IControl, ctrl)
+# True
+# >>> ctrl.value is None
+# True
+# >>> import cStringIO
+#
+# >>> ctrl.add_file(cStringIO.StringIO('File contents'),
+# ... 'text/plain', 'test.txt')
+#
+# The file control (like the other controls) also knows if it is disabled
+# or if it can have multiple values.
+#
+# >>> ctrl.disabled
+# False
+# >>> ctrl.multiple
+# False
+#
+# - Selection Control (Single-Valued)
+#
+# >>> ctrl = browser.getControl('Single Select Control')
+# >>> ctrl
+# <ListControl name='single-select-value' type='select'>
+# >>> verifyObject(zc.testbrowser.interfaces.IListControl, ctrl)
+# True
+# >>> ctrl.value
+# ['1']
+# >>> ctrl.value = ['2']
+# >>> ctrl.disabled
+# False
+# >>> ctrl.multiple
+# False
+# >>> ctrl.options
+# ['1', '2', '3']
+# >>> ctrl.displayOptions
+# ['Uno', 'Dos', 'Third']
+# >>> ctrl.displayValue
+# ['Dos']
+# >>> ctrl.displayValue = ['Tres']
+# >>> ctrl.displayValue
+# ['Third']
+# >>> ctrl.displayValue = ['Dos']
+# >>> ctrl.displayValue
+# ['Dos']
+# >>> ctrl.displayValue = ['Third']
+# >>> ctrl.displayValue
+# ['Third']
+# >>> ctrl.value
+# ['3']
+#
+# - Selection Control (Multi-Valued)
+#
+# This was already demonstrated in the introduction to control objects above.
+#
+# - Checkbox Control (Single-Valued; Unvalued)
+#
+# >>> ctrl = browser.getControl(name='single-unvalued-checkbox-value')
+# >>> ctrl
+# <ListControl name='single-unvalued-checkbox-value' type='checkbox'>
+# >>> verifyObject(zc.testbrowser.interfaces.IListControl, ctrl)
+# True
+# >>> ctrl.value
+# True
+# >>> ctrl.value = False
+# >>> ctrl.disabled
+# False
+# >>> ctrl.multiple
+# True
+# >>> ctrl.options
+# [True]
+# >>> ctrl.displayOptions
+# ['Single Unvalued Checkbox']
+# >>> ctrl.displayValue
+# []
+# >>> verifyObject(
+# ... zc.testbrowser.interfaces.IItemControl,
+# ... browser.getControl('Single Unvalued Checkbox'))
+# True
+# >>> browser.getControl('Single Unvalued Checkbox').optionValue
+# 'on'
+# >>> browser.getControl('Single Unvalued Checkbox').selected
+# False
+# >>> ctrl.displayValue = ['Single Unvalued Checkbox']
+# >>> ctrl.displayValue
+# ['Single Unvalued Checkbox']
+# >>> browser.getControl('Single Unvalued Checkbox').selected
+# True
+# >>> browser.getControl('Single Unvalued Checkbox').selected = False
+# >>> browser.getControl('Single Unvalued Checkbox').selected
+# False
+# >>> ctrl.displayValue
+# []
+# >>> browser.getControl(
+# ... name='single-disabled-unvalued-checkbox-value').disabled
+# True
+#
+# - Checkbox Control (Single-Valued, Valued)
+#
+# >>> ctrl = browser.getControl(name='single-valued-checkbox-value')
+# >>> ctrl
+# <ListControl name='single-valued-checkbox-value' type='checkbox'>
+# >>> verifyObject(zc.testbrowser.interfaces.IListControl, ctrl)
+# True
+# >>> ctrl.value
+# ['1']
+# >>> ctrl.value = []
+# >>> ctrl.disabled
+# False
+# >>> ctrl.multiple
+# True
+# >>> ctrl.options
+# ['1']
+# >>> ctrl.displayOptions
+# ['Single Valued Checkbox']
+# >>> ctrl.displayValue
+# []
+# >>> verifyObject(
+# ... zc.testbrowser.interfaces.IItemControl,
+# ... browser.getControl('Single Valued Checkbox'))
+# True
+# >>> browser.getControl('Single Valued Checkbox').selected
+# False
+# >>> browser.getControl('Single Valued Checkbox').optionValue
+# '1'
+# >>> ctrl.displayValue = ['Single Valued Checkbox']
+# >>> ctrl.displayValue
+# ['Single Valued Checkbox']
+# >>> browser.getControl('Single Valued Checkbox').selected
+# True
+# >>> browser.getControl('Single Valued Checkbox').selected = False
+# >>> browser.getControl('Single Valued Checkbox').selected
+# False
+# >>> ctrl.displayValue
+# []
+#
+# - Checkbox Control (Multi-Valued)
+#
+# >>> ctrl = browser.getControl(name='multi-checkbox-value')
+# >>> ctrl
+# <ListControl name='multi-checkbox-value' type='checkbox'>
+# >>> verifyObject(zc.testbrowser.interfaces.IListControl, ctrl)
+# True
+# >>> ctrl.value
+# ['1', '3']
+# >>> ctrl.value = ['1', '2']
+# >>> ctrl.disabled
+# False
+# >>> ctrl.multiple
+# True
+# >>> ctrl.options
+# ['1', '2', '3']
+# >>> ctrl.displayOptions
+# ['One', 'Two', 'Three']
+# >>> ctrl.displayValue
+# ['One', 'Two']
+# >>> ctrl.displayValue = ['Two']
+# >>> ctrl.value
+# ['2']
+# >>> browser.getControl('Two').optionValue
+# '2'
+# >>> browser.getControl('Two').selected
+# True
+# >>> verifyObject(zc.testbrowser.interfaces.IItemControl,
+# ... browser.getControl('Two'))
+# True
+# >>> browser.getControl('Three').selected = True
+# >>> browser.getControl('Three').selected
+# True
+# >>> browser.getControl('Two').selected
+# True
+# >>> ctrl.value
+# ['2', '3']
+# >>> browser.getControl('Two').selected = False
+# >>> ctrl.value
+# ['3']
+# >>> browser.getControl('Three').selected = False
+# >>> ctrl.value
+# []
+#
+# - Radio Control
+#
+# This is how you get a radio button based control:
+#
+# >>> ctrl = browser.getControl(name='radio-value')
+#
+# This shows the existing value of the control, as it was in the
+# HTML received from the server:
+#
+# >>> ctrl.value
+# ['2']
+#
+# We can then unselect it:
+#
+# >>> ctrl.value = []
+# >>> ctrl.value
+# []
+#
+# We can also reselect it:
+#
+# >>> ctrl.value = ['2']
+# >>> ctrl.value
+# ['2']
+#
+# displayValue shows the text the user would see next to the
+# control:
+#
+# >>> ctrl.displayValue
+# ['Zwei']
+#
+# This is just unit testing:
+#
+# >>> ctrl
+# <ListControl name='radio-value' type='radio'>
+# >>> verifyObject(zc.testbrowser.interfaces.IListControl, ctrl)
+# True
+# >>> ctrl.disabled
+# False
+# >>> ctrl.multiple
+# False
+# >>> ctrl.options
+# ['1', '2', '3']
+# >>> ctrl.displayOptions
+# ['Ein', 'Zwei', 'Drei']
+# >>> ctrl.displayValue = ['Ein']
+# >>> ctrl.value
+# ['1']
+# >>> ctrl.displayValue
+# ['Ein']
+#
+# The radio control subcontrols were illustrated above.
+#
+# - Image Control
+#
+# >>> ctrl = browser.getControl(name='image-value')
+# >>> ctrl
+# <ImageControl name='image-value' type='image'>
+# >>> verifyObject(zc.testbrowser.interfaces.IImageSubmitControl, ctrl)
+# True
+# >>> ctrl.value
+# ''
+# >>> ctrl.disabled
+# False
+# >>> ctrl.multiple
+# False
+#
+# - Submit Control
+#
+# >>> ctrl = browser.getControl(name='submit-value')
+# >>> ctrl
+# <SubmitControl name='submit-value' type='submit'>
+# >>> browser.getControl('Submit This') # value of submit button is a label
+# <SubmitControl name='submit-value' type='submit'>
+# >>> browser.getControl('Standard Submit Control') # label tag is legal
+# <SubmitControl name='submit-value' type='submit'>
+# >>> browser.getControl('Submit') # multiple labels, but same control
+# <SubmitControl name='submit-value' type='submit'>
+# >>> verifyObject(zc.testbrowser.interfaces.ISubmitControl, ctrl)
+# True
+# >>> ctrl.value
+# 'Submit This'
+# >>> ctrl.disabled
+# False
+# >>> ctrl.multiple
+# False
+#
+#Using Submitting Controls
+#~~~~~~~~~~~~~~~~~~~~~~~~~
+#
+#Both the submit and image type should be clickable and submit the form:
+#
+# >>> browser.getControl('Text Control').value = 'Other Text'
+# >>> browser.getControl('Submit').click()
+# >>> browser.contents
+# "...'text-value': ['Other Text']..."
+#
+#Note that if you click a submit object after the associated page has expired,
+#you will get an error.
+#
+# >>> browser.open('controls.html')
+# >>> ctrl = browser.getControl('Submit')
+# >>> ctrl.click()
+# >>> ctrl.click()
+# Traceback (most recent call last):
+# ...
+# ExpiredError
+#
+#All the above also holds true for the image control:
+#
+# >>> browser.open('controls.html')
+# >>> browser.getControl('Text Control').value = 'Other Text'
+# >>> browser.getControl(name='image-value').click()
+# >>> browser.contents
+# "...'text-value': ['Other Text']..."
+#
+# >>> browser.open('controls.html')
+# >>> ctrl = browser.getControl(name='image-value')
+# >>> ctrl.click()
+# >>> ctrl.click()
+# Traceback (most recent call last):
+# ...
+# ExpiredError
+#
+#But when sending an image, you can also specify the coordinate you clicked:
+#
+# >>> browser.open('controls.html')
+# >>> browser.getControl(name='image-value').click((50,25))
+# >>> browser.contents
+# "...'image-value.x': ['50']...'image-value.y': ['25']..."
+#
+#Forms
+#-----
+#
+#Because pages can have multiple forms with like-named controls, it is sometimes
+#necessary to access forms by name or id. The browser's `forms` attribute can
+#be used to do so. The key value is the form's name or id. If more than one
+#form has the same name or id, the first one will be returned.
+#
+# >>> browser.open('forms.html')
+# >>> form = browser.getForm(name='one')
+#
+#Form instances conform to the IForm interface.
+#
+# >>> verifyObject(zc.testbrowser.interfaces.IForm, form)
+# True
+#
+#The form exposes several attributes related to forms:
+#
+# - The name of the form:
+#
+# >>> form.name
+# 'one'
+#
+# - The id of the form:
+#
+# >>> form.id
+# '1'
+#
+# - The action (target URL) when the form is submitted:
+#
+# >>> form.action
+# 'http://localhost:.../forms.html'
+#
+# - The method (HTTP verb) used to transmit the form data:
+#
+# >>> form.method
+# 'POST'
+#
+# - The encoding type of the form data:
+#
+# >>> form.enctype
+# 'application/x-www-form-urlencoded'
+#
+#Besides those attributes, you have also a couple of methods. Like for the
+#browser, you can get control objects, but limited to the current form...
+#
+# >>> form.getControl(name='text-value')
+# <Control name='text-value' type='text'>
+#
+#...and submit the form.
+#
+# >>> form.submit('Submit')
+# >>> browser.contents
+# "...'text-value': ['First Text']..."
+#
+#Submitting also works without specifying a control, as shown below, which is
+#it's primary reason for existing in competition with the control submission
+#discussed above.
+#
+#Now let me show you briefly that looking up forms is sometimes important. In
+#the `forms.html` template, we have four forms all having a text control named
+#`text-value`. Now, if I use the browser's `get` method,
+#
+# >>> browser.open('forms.html')
+# >>> browser.getControl(name='text-value')
+# Traceback (most recent call last):
+# ...
+# AmbiguityError: name 'text-value'
+# >>> browser.getControl('Text Control')
+# Traceback (most recent call last):
+# ...
+# AmbiguityError: label 'Text Control'
+#
+#I'll always get an ambiguous form field. I can use the index argument, or
+#with the `getForm` method I can disambiguate by searching only within a given
+#form:
+#
+# >>> form = browser.getForm('2')
+# >>> form.getControl(name='text-value').value
+# 'Second Text'
+# >>> form.submit('Submit')
+# >>> browser.contents
+# "...'text-value': ['Second Text']..."
+# >>> browser.open('forms.html')
+# >>> form = browser.getForm('2')
+# >>> form.getControl('Submit').click()
+# >>> browser.contents
+# "...'text-value': ['Second Text']..."
+# >>> browser.open('forms.html')
+# >>> browser.getForm('3').getControl('Text Control').value
+# 'Third Text'
+#
+#The last form on the page does not have a name, an id, or a submit button.
+#Working with it is still easy, thanks to a index attribute that guarantees
+#order. (Forms without submit buttons are sometimes useful for JavaScript.)
+#
+# >>> form = browser.getForm(index=3)
+# >>> form.submit()
+# >>> browser.contents
+# "...'text-value': ['Fourth Text']..."
+#
+#If a form is requested that does not exists, an exception will be raised.
+#
+# >>> browser.open('forms.html')
+# >>> form = browser.getForm('does-not-exist')
+# Traceback (most recent call last):
+# LookupError
+#
+#If the HTML page contains only one form, no arguments to `getForm` are
+#needed:
+#
+# >>> browser.open('oneform.html')
+# >>> browser.getForm()
+# <zc.testbrowser...Form object at ...>
+#
+#If the HTML page contains more than one form, `index` is needed to
+#disambiguate if no other arguments are provided:
+#
+# >>> browser.open('forms.html')
+# >>> browser.getForm()
+# Traceback (most recent call last):
+# ValueError: if no other arguments are given, index is required.
+#
+#
+#Performance Testing
+#-------------------
+#
+#Browser objects keep up with how much time each request takes. This can be
+#used to ensure a particular request's performance is within a tolerable range.
+#Be very careful using raw seconds, cross-machine differences can be huge,
+#pystones is usually a better choice.
+#
+# >>> browser.open('index.html')
+# >>> browser.lastRequestSeconds < 10 # really big number for safety
+# True
+# >>> browser.lastRequestPystones < 10000 # really big number for safety
+# True
+#
+#
+#Hand-Holding
+#------------
+#
+#Instances of the various objects ensure that users don't set incorrect
+#instance attributes accidentally.
+#
+# >>> browser.nonexistant = None
+# Traceback (most recent call last):
+# ...
+# AttributeError: 'Browser' object has no attribute 'nonexistant'
+#
+# >>> form.nonexistant = None
+# Traceback (most recent call last):
+# ...
+# AttributeError: 'Form' object has no attribute 'nonexistant'
+#
+# >>> control.nonexistant = None
+# Traceback (most recent call last):
+# ...
+# AttributeError: 'Control' object has no attribute 'nonexistant'
+#
+# >>> link.nonexistant = None
+# Traceback (most recent call last):
+# ...
+# AttributeError: 'Link' object has no attribute 'nonexistant'
+#
+#
+#Fixed Bugs
+#----------
+#
+#This section includes tests for bugs that were found and then fixed that don't
+#fit into the more documentation-centric sections above.
+#
+#Spaces in URL
+#~~~~~~~~~~~~~
+#
+#When URLs have spaces in them, they're handled correctly (before the bug was
+#fixed, you'd get "ValueError: too many values to unpack"):
+#
+# >>> browser.open('navigate.html')
+# >>> browser.getLink('Spaces in the URL').click()
+#
+#.goBack() Truncation
+#~~~~~~~~~~~~~~~~~~~~
+#
+#The .goBack() method used to truncate the .contents.
+#
+# >>> browser.open('navigate.html')
+# >>> actual_length = len(browser.contents)
+#
+# >>> browser.open('navigate.html')
+# >>> browser.open('index.html')
+# >>> browser.goBack()
+# >>> len(browser.contents) == actual_length
+# True
Modified: zc.testbrowser/trunk/src/zc/testbrowser/interfaces.py
===================================================================
--- zc.testbrowser/trunk/src/zc/testbrowser/interfaces.py 2007-09-24 14:09:03 UTC (rev 79877)
+++ zc.testbrowser/trunk/src/zc/testbrowser/interfaces.py 2007-09-24 14:20:43 UTC (rev 79878)
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (c) 2005 Zope Corporation and Contributors.
+# Copyright (c) 2005-2007 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
@@ -17,7 +17,7 @@
class LinkNotFoundError(ValueError):
- pass
+ """Exception raised if a link could not be found for the given parameter."""
class IBrowser(interface.Interface):
Modified: zc.testbrowser/trunk/src/zc/testbrowser/real.js
===================================================================
--- zc.testbrowser/trunk/src/zc/testbrowser/real.js 2007-09-24 14:09:03 UTC (rev 79877)
+++ zc.testbrowser/trunk/src/zc/testbrowser/real.js 2007-09-24 14:20:43 UTC (rev 79878)
@@ -5,6 +5,13 @@
document.getElementById("appcontent"
).addEventListener("load", function() { tb_page_loaded = true; }, true);
+function tb_xpath(pattern, context) {
+ if (context == null)
+ context = content.document;
+ return content.document.evaluate(
+ pattern, context, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+}
+
function tb_get_link_by_predicate(predicate, index) {
var anchors = content.document.getElementsByTagName('a');
var i=0;
@@ -50,7 +57,6 @@
text = tb_normalize_whitespace(text);
return tb_get_link_by_predicate(
function (a) {
- //alert(tb_normalize_whitespace(a.textContent) + '|' + text + '|' + tb_normalize_whitespace(a.textContent).indexOf(text));
return tb_normalize_whitespace(a.textContent).indexOf(text) != -1;
}, index)
}
@@ -109,3 +115,199 @@
function tb_get_link_text(token) {
return tb_normalize_whitespace(tb_tokens[token].textContent);
}
+
+function tb_get_control_by_predicate(
+ predicate, index, allowDuplicate, context, xpath) {
+ if (xpath == null) {
+ var xpath = '//input | //select | //option | //textarea';
+ }
+ var res = tb_xpath(xpath, context)
+ var i=0;
+ var found = null;
+ if (index == undefined) index = null;
+ for (var x = 0; x < res.snapshotLength; x++) {
+ elem = res.snapshotItem(x);
+ if (!predicate(elem)) {
+ continue;
+ }
+ if (context)
+ alert(context.getAttribute('name') + ' | ' + elem.parentNode.getAttribute('name') + ' | ' + i);
+ // if we weren't given an index, but we found more than
+ // one match, we have an ambiguity
+ if (index == null && i > 0) {
+ return 'ambiguity error';
+ }
+ found = elem;
+
+ // if we were given an index and we just found it, stop
+ if (index != null && i == index) {
+ break;
+ }
+
+ // One exception is when the name of a radio or checkbox input is
+ // found twice
+ if (allowDuplicate) {
+ inputType = elem.getAttribute('type');
+ if (inputType == 'radio' || inputType == 'checkbox') {
+ break;
+ }
+ }
+ i++;
+ }
+ if (found != null) {
+ tb_tokens[tb_next_token] = found;
+ return tb_next_token++;
+ }
+ return false; // control not found
+}
+
+
+function tb_get_control_by_label(text, index, contextToken, xpath) {
+ context = null;
+ if (contextToken != null) {
+ context = tb_tokens[contextToken];
+ }
+ text = tb_normalize_whitespace(text);
+ return tb_get_control_by_predicate(
+ function (control) {
+ var tag = control.tagName;
+ var labelText = null;
+ if (tag == 'OPTION') {
+ labelText = control.textContent;
+ if (control.hasAttribute('label')) {
+ labelText += ' ' + control.getAttribute('label');
+ }
+ }
+ else if (tag == 'SUBMIT' || tag == 'BUTTON') {
+ labelText = control.getAttribute('value');
+ }
+ else {
+ var id = control.getAttribute('id');
+ var name = control.getAttribute('name');
+ // The label element references the control id
+ var res = tb_xpath("//label[@for='" + id + "']")
+ // The label encloses the input element
+ if (res.snapshotLength == 0) {
+ var res = tb_xpath("ancestor::label", control);
+ }
+ // Collect all text content, since HTML allows multiple labels
+ // for the same input.
+ if (res.snapshotLength > 0) {
+ labelText = '';
+ for (var c = 0; c < res.snapshotLength; c++) {
+ labelText += ' ' + tb_normalize_whitespace(
+ res.snapshotItem(c).textContent);
+ }
+ }
+ }
+ // We can only match whole words! Sigh!
+ if (labelText == null)
+ return false;
+ var expr = ('(^| )\\W*' +
+ text.replace(/(\W)/gi, '\\$1') +
+ '\\W*($| [^a-zA-Z]*)');
+ if (labelText.search(expr) == -1)
+ return false;
+ return true;
+ }, index, false, context, xpath)
+}
+
+function tb_get_control_by_name(name, index) {
+ return tb_get_control_by_predicate(
+ function (control) {
+ var controlName = control.getAttribute('name');
+ return controlName != null && controlName.indexOf(name) != -1;
+ }, index, true)
+}
+
+function tb_get_listcontrol_options(token) {
+ var elem = tb_tokens[token];
+ var tagName = elem.tagName;
+ var options = new Array();
+ if (tagName == 'SELECT') {
+ var res = tb_xpath('child::option', elem)
+ for (var c = 0; c < res.snapshotLength; c++) {
+ options.push(res.snapshotItem(c).getAttribute('value'));
+ }
+ }
+ return options.toSource();
+}
+
+function tb_get_listcontrol_displayOptions(token) {
+ var elem = tb_tokens[token];
+ var tagName = elem.tagName;
+ var options = new Array();
+ if (tagName == 'SELECT') {
+ var res = tb_xpath('child::option', elem)
+ for (var c = 0; c < res.snapshotLength; c++) {
+ item = res.snapshotItem(c)
+ if (item.hasAttribute('label'))
+ options.push(item.getAttribute('label'))
+ else
+ options.push(item.textContent);
+ }
+ }
+ return options.toSource();
+}
+
+function tb_get_listcontrol_value(token) {
+ var elem = tb_tokens[token];
+ var tagName = elem.tagName;
+ var options = new Array();
+ if (tagName == 'SELECT') {
+ var res = tb_xpath('child::option', elem)
+ for (var c = 0; c < res.snapshotLength; c++) {
+ var item = res.snapshotItem(c);
+ if (item.selected)
+ options.push(res.snapshotItem(c).getAttribute('value'));
+ }
+ }
+ return options.toSource();
+}
+
+function tb_get_listcontrol_displayValue(token) {
+ var elem = tb_tokens[token];
+ var tagName = elem.tagName;
+ var options = new Array();
+ if (tagName == 'SELECT') {
+ var res = tb_xpath('child::option', elem)
+ for (var c = 0; c < res.snapshotLength; c++) {
+ var item = res.snapshotItem(c);
+ if (item.selected)
+ options.push(item.textContent);
+ }
+ }
+ return options.toSource();
+}
+
+function tb_set_listcontrol_displayValue(token, value) {
+ var elem = tb_tokens[token];
+ var tagName = elem.tagName;
+ var options = new Array();
+ if (tagName == 'SELECT') {
+ var res = tb_xpath('child::option', elem)
+ for (var c = 0; c < res.snapshotLength; c++) {
+ var item = res.snapshotItem(c);
+ if (value.indexOf(item.textContent) != -1)
+ item.selected = true;
+ else
+ item.selected = false;
+ }
+ }
+ return options.toSource();
+}
+
+function tb_get_listcontrol_item_tokens(token) {
+ var elem = tb_tokens[token];
+ var tagName = elem.tagName;
+ var tokens = new Array();
+ if (tagName == 'SELECT') {
+ var res = tb_xpath('child::option', elem);
+ for (var c = 0; c < res.snapshotLength; c++) {
+ tb_tokens[tb_next_token] = res.snapshotItem(c);
+ tokens.push(tb_next_token++);
+ }
+ }
+ return tokens.toSource();
+}
+
Modified: zc.testbrowser/trunk/src/zc/testbrowser/real.py
===================================================================
--- zc.testbrowser/trunk/src/zc/testbrowser/real.py 2007-09-24 14:09:03 UTC (rev 79877)
+++ zc.testbrowser/trunk/src/zc/testbrowser/real.py 2007-09-24 14:20:43 UTC (rev 79878)
@@ -15,6 +15,27 @@
class BrowserStateError(RuntimeError):
pass
+def controlFactory(token, browser, selectionItem=False):
+ tagName = browser.execute('tb_tokens[%s].tagName' % token).lower()
+ if tagName == 'select':
+ return ListControl(token, browser)
+ elif tagName == 'option':
+ return ItemControl(token, browser)
+
+ inputType = browser.execute(
+ 'tb_tokens[%s].getAttribute("type")' % token).lower()
+ if inputType in ('checkbox', 'radio'):
+ if selectionItem:
+ return ItemControl(token, browser)
+ return ListControl(token, browser)
+ elif inputType in ('submit', 'button'):
+ return SubmitControl(token, browser)
+ elif inputType == 'image':
+ return ImageControl(token, browser)
+
+ return Control(token, browser)
+
+
class Browser(zc.testbrowser.browser.SetattrErrorsMixin):
zope.interface.implements(zc.testbrowser.interfaces.IBrowser)
@@ -41,16 +62,16 @@
' Is MozRepl running?' % (host, port))
self.telnet.write(open(js_path, 'rt').read())
- self.expect([PROMPT])
+ self.expect()
def execute(self, js):
if not js.strip():
return
self.telnet.write("'MARKER'")
self.telnet.read_until('MARKER', self.timeout)
- self.expect([PROMPT])
+ self.expect()
self.telnet.write(js)
- i, match, text = self.expect([PROMPT])
+ i, match, text = self.expect()
if '!!!' in text: import pdb;pdb.set_trace() # XXX debug only, remove
result = text.rsplit('\n', 1)
if len(result) == 1:
@@ -63,7 +84,7 @@
for line in lines:
self.execute(line)
- def expect(self, res):
+ def expect(self):
i, match, text = self.telnet.expect([PROMPT], self.timeout)
if match is None:
raise RuntimeError('unexpected result from MozRepl')
@@ -182,6 +203,7 @@
if token == 'false':
raise zc.testbrowser.interfaces.LinkNotFoundError
elif token == 'ambiguity error':
+ # XXX: Should not depend on client form.
raise ClientForm.AmbiguityError(msg)
return Link(token, self)
@@ -190,8 +212,35 @@
self.execute('tb_follow_link(%s)' % token)
def getControl(self, label=None, name=None, index=None):
- raise NotImplementedError
+ zc.testbrowser.browser.onlyOne([label, name], '"label" and "name"')
+ js_index = simplejson.dumps(index)
+ selectionItem = False
+ if label is not None:
+ msg = 'label %r' % label
+ token = self.execute('tb_get_control_by_label(%s, %s)'
+ % (simplejson.dumps(label), js_index))
+ if (token not in ('false', 'ambiguity error')):
+ inputType = self.execute(
+ 'tb_tokens[%s].getAttribute("type")' % token)
+ if inputType and inputType.lower() in ('radio', 'checkbox'):
+ selectionItem = True
+ elif name is not None:
+ msg = 'name %r' % name
+ token = self.execute('tb_get_control_by_name(%s, %s)'
+ % (simplejson.dumps(name), js_index))
+ elif id is not None:
+ msg = 'id %r' % id
+ token = self.execute('tb_get_control_by_id(%s, %s)'
+ % (simplejson.dumps(id), js_index))
+ if token == 'false':
+ raise LookupError(msg)
+ elif token == 'ambiguity error':
+ # XXX: Should not depend on client form.
+ raise ClientForm.AmbiguityError(msg)
+
+ return controlFactory(token, self, selectionItem)
+
def getForm(self, id=None, name=None, action=None, index=None):
raise NotImplementedError
@@ -224,3 +273,267 @@
def __repr__(self):
return "<%s text=%r url=%r>" % (
self.__class__.__name__, self.text, self.url)
+
+
+class Control(zc.testbrowser.browser.SetattrErrorsMixin):
+ """A control of a form."""
+ zope.interface.implements(zc.testbrowser.interfaces.IControl)
+
+ _enable_setattr_errors = False
+
+ def __init__(self, token, browser):
+ self.token = token
+ self.browser = browser
+ self._browser_counter = self.browser._counter
+
+ # disable addition of further attributes
+ self._enable_setattr_errors = True
+
+ @property
+ def disabled(self):
+ return self.browser.execute(
+ 'tb_tokens[%s].hasAttribute("disabled")' % self.token) == 'true'
+
+ @property
+ def type(self):
+ return self.browser.execute(
+ 'tb_tokens[%s].getAttribute("type")' % self.token)
+
+ @property
+ def name(self):
+ return self.browser.execute(
+ 'tb_tokens[%s].getAttribute("name")' % self.token)
+
+ @property
+ def multiple(self):
+ return self.browser.execute(
+ 'tb_tokens[%s].hasAttribute("multiple")' % self.token) == 'true'
+
+ @apply
+ def value():
+
+ def fget(self):
+ return self.browser.execute(
+ 'tb_tokens[%s].getAttribute("value")' % self.token)
+
+ def fset(self, value):
+ if self._browser_counter != self.browser._counter:
+ raise zc.testbrowser.interfaces.ExpiredError
+ if self.type == 'file':
+ self.add_file(value, content_type=self.content_type,
+ filename=self.filename)
+ elif self.type == 'checkbox' and len(self.mech_control.items) == 1:
+ self.mech_control.items[0].selected = bool(value)
+ else:
+ self.browser.execute(
+ 'tb_tokens[%s].setAttribute("value", %s)' %(
+ self.token, simplejson.dumps(value)))
+ return property(fget, fset)
+
+ def add_file(self, file, content_type, filename):
+ if not self.mech_control.type == 'file':
+ raise TypeError("Can't call add_file on %s controls"
+ % self.mech_control.type)
+ if isinstance(file, str):
+ file = StringIO(file)
+ self.mech_control.add_file(file, content_type, filename)
+
+ def clear(self):
+ if self._browser_counter != self.browser._counter:
+ raise zc.testbrowser.interfaces.ExpiredError
+ self.mech_control.clear()
+
+ def __repr__(self):
+ return "<%s name=%r type=%r>" % (
+ self.__class__.__name__, self.name, self.type)
+
+
+class ListControl(Control):
+ zope.interface.implements(zc.testbrowser.interfaces.IListControl)
+
+ @property
+ def type(self):
+ tagName = self.browser.execute(
+ 'tb_tokens[%s].tagName' % self.token).lower()
+ if tagName == 'input':
+ return super(ListControl, self).type
+ return tagName
+
+ @apply
+ def displayValue():
+ # not implemented for anything other than select;
+ # would be nice if ClientForm implemented for checkbox and radio.
+ # attribute error for all others.
+ def fget(self):
+ options = self.browser.execute(
+ 'tb_get_listcontrol_displayValue(%r)' % self.token)
+ return [str(option) for option in simplejson.loads(options)]
+
+ def fset(self, value):
+ if self._browser_counter != self.browser._counter:
+ raise zc.testbrowser.interfaces.ExpiredError
+ self.browser.execute(
+ 'tb_set_listcontrol_displayValue(%r, %s)' % (
+ self.token, simplejson.dumps(value)) )
+ return property(fget, fset)
+
+ @apply
+ def value():
+ def fget(self):
+ options = self.browser.execute(
+ 'tb_get_listcontrol_value(%r)' % self.token)
+ return [str(option) for option in simplejson.loads(options)]
+
+ def fset(self, value):
+ if self._browser_counter != self.browser._counter:
+ raise zc.testbrowser.interfaces.ExpiredError
+ self.browser.execute(
+ 'tb_set_listcontrol_value(%r, %s)' % (
+ self.token, simplejson.dumps(value)) )
+ return property(fget, fset)
+
+ @property
+ def displayOptions(self):
+ options = self.browser.execute(
+ 'tb_get_listcontrol_displayOptions(%r)' % self.token)
+ return [str(option) for option in simplejson.loads(options)]
+
+ @property
+ def options(self):
+ options = self.browser.execute(
+ 'tb_get_listcontrol_options(%r)' % self.token)
+ return [str(option) for option in simplejson.loads(options)]
+
+ @property
+ def controls(self):
+ if self._browser_counter != self.browser._counter:
+ raise zc.testbrowser.interfaces.ExpiredError
+ res = []
+ tokens = self.browser.execute(
+ 'tb_get_listcontrol_item_tokens(%r)' % self.token)
+ return [ItemControl(token, self.browser)
+ for token in simplejson.loads(tokens)]
+
+
+ def getControl(self, label=None, value=None, index=None):
+ if self._browser_counter != self.browser._counter:
+ raise zc.testbrowser.interfaces.ExpiredError
+
+ zc.testbrowser.browser.onlyOne([label, value], '"label" and "value"')
+
+ js_index = simplejson.dumps(index)
+ selectionItem = False
+ if label is not None:
+ msg = 'label %r' % label
+ token = self.browser.execute(
+ 'tb_get_control_by_label(%s, %s, %r, "//input | //option")'
+ % (simplejson.dumps(label), js_index, self.token))
+ if (token not in ('false', 'ambiguity error')):
+ inputType = self.browser.execute(
+ 'tb_tokens[%s].getAttribute("type")' % token)
+ if inputType and inputType.lower() in ('radio', 'checkbox'):
+ selectionItem = True
+ elif name is not None:
+ msg = 'name %r' % name
+ token = self.browser.execute('tb_get_control_by_name(%s, %s)'
+ % (simplejson.dumps(name), js_index))
+ elif id is not None:
+ msg = 'id %r' % id
+ token = self.browser.execute('tb_get_control_by_id(%s, %s)'
+ % (simplejson.dumps(id), js_index))
+
+ if token == 'false':
+ raise LookupError(msg)
+ elif token == 'ambiguity error':
+ # XXX: Should not depend on client form.
+ raise ClientForm.AmbiguityError(msg)
+
+ return controlFactory(token, self.browser, selectionItem)
+
+
+class SubmitControl(Control):
+ zope.interface.implements(zc.testbrowser.interfaces.ISubmitControl)
+
+ def click(self):
+ if self._browser_counter != self.browser._counter:
+ raise zc.testbrowser.interfaces.ExpiredError
+ self.browser._clickSubmit(self.mech_form, self.mech_control, (1,1))
+ self.browser._changed()
+
+
+class ImageControl(Control):
+ zope.interface.implements(zc.testbrowser.interfaces.IImageSubmitControl)
+
+ def click(self, coord=(1,1)):
+ if self._browser_counter != self.browser._counter:
+ raise zc.testbrowser.interfaces.ExpiredError
+ self.browser._clickSubmit(self.mech_form, self.mech_control, coord)
+ self.browser._changed()
+
+
+class ItemControl(zc.testbrowser.browser.SetattrErrorsMixin):
+ zope.interface.implements(zc.testbrowser.interfaces.IItemControl)
+
+ def __init__(self, token, browser):
+ self.token = token
+ self.browser = browser
+ self._browser_counter = self.browser._counter
+ # disable addition of further attributes
+ self._enable_setattr_errors = True
+
+ @property
+ def control(self):
+ if self._browser_counter != self.browser._counter:
+ raise zc.testbrowser.interfaces.ExpiredError
+ res = controlFactory(
+ self.mech_item._control, self.mech_form, self.browser)
+ self.__dict__['control'] = res
+ return res
+
+ @property
+ def disabled(self):
+ return self.mech_item.disabled
+
+ @apply
+ def selected():
+
+ def fget(self):
+ tagName = self.browser.execute(
+ 'tb_tokens[%s].tagName' % self.token)
+ if tagName == 'OPTION':
+ return self.browser.execute(
+ 'tb_tokens[%s].selected' % self.token) == 'true'
+ return self.browser.execute(
+ 'tb_tokens[%s].hasAttribute("checked")' % self.token) == 'true'
+
+ def fset(self, value):
+ if self._browser_counter != self.browser._counter:
+ raise zc.testbrowser.interfaces.ExpiredError
+ self.mech_item.selected = value
+
+ return property(fget, fset)
+
+ @property
+ def optionValue(self):
+ return self.browser.execute(
+ 'tb_tokens[%s].getAttribute("value")' % self.token)
+
+ def click(self):
+ if self._browser_counter != self.browser._counter:
+ raise zc.testbrowser.interfaces.ExpiredError
+ self.mech_item.selected = not self.mech_item.selected
+
+ def __repr__(self):
+ tagName = self.browser.execute('tb_tokens[%s].tagName' % self.token)
+ if tagName.lower() == 'option':
+ type = 'select'
+ name = self.browser.execute(
+ 'tb_tokens[%s].parentNode.getAttribute("name")' % self.token)
+ else:
+ type = self.browser.execute(
+ 'tb_tokens[%s].getAttribute("type")' % self.token)
+ name = self.browser.execute(
+ 'tb_tokens[%s].getAttribute("name")' % self.token)
+ return "<%s name=%r type=%r optionValue=%r selected=%r>" % (
+ self.__class__.__name__, name, type, self.optionValue,
+ self.selected)
More information about the Checkins
mailing list