webtest.sel – Functional Testing with Selenium

Routines for testing WSGI applications with selenium.

Most interesting is SeleniumApp and the selenium() decorator

Environment variables

Those value are used if found in environment:

  • SELENIUM_HOST: Default to 127.0.0.1

  • SELENIUM_PORT: Default to 4444

  • SELENIUM_BIND: IP used to bind extra servers (WSGI Server/File server). Default to 127.0.0.1

  • SELENIUM_DRIVER: The driver used to start the browser. Usualy something in *chrome, *firefox, *googlechrome. Default to *googlechrome. You can get the full list by running:

    $ java -jar selenium-server.jar -interactive
    cmd=getNewBrowserSession
  • SELENIUM_KEEP_OPEN: If exist then browser session are not closed so you can introspect the problem on failure.

  • SELENIUM_JAR: If selenium is not running then this jar is used to run selenium.

Module Contents

class webtest.sel.SeleniumApp(app=None, url=None, timeout=30000, extra_environ=None, relative_to=None, **kwargs)

See webtest.TestApp

SeleniumApp only support GET requests

browser

The current Selenium

close()

Close selenium and the WSGI server if needed

class webtest.sel.Selenium

Selenium RC control aka browser

A object use to manipulate DOM nodes. This object allow to use the underlying selenium api. See Selenium api

You can use the original method name:

browser.fireEvent('id=#myid", 'focus')

Or a more pythonic name:

browser.fire_event('id=#myid", 'focus')

Both are equal to:

browser.execute('fireEvent', 'id=#myid', 'focus')
webtest.sel.selenium(obj)

A callable usable as:

  • class decorator
  • function decorator
  • contextmanager

Return Values

Some of the return values return instances of these classes:

class webtest.sel.TestResponse(body=None, status=None, headerlist=None, app_iter=None, request=None, content_type=None, conditional_response=None, **kw)
doc

Expose a Document

follow(status=None, **kw)

If this request is a redirect, follow that redirect. It is an error if this is not a redirect response. Returns another response object.

class webtest.sel.Document(resp)

The browser document. resp.doc.myid is egual to resp.doc.css('#myid')

button(description=None, buttonid=None, index=None)

Get a button

css(selector)

Get an Element using a css selector

get(tag, **kwargs)

Return an element matching tag, an attribute and an index. For example:

resp.doc.get('input', name='go') => xpath=//input[@name="go"]
resp.doc.get('li', description='Item') => xpath=//li[.="Item"]
input(value=None, name=None, inputid=None, index=None)

Get an input field

Get a link

xpath(path)

Get an Element using xpath

class webtest.sel.Element(resp, locator)

A object use to manipulate DOM nodes. This object allow to use the underlying selenium api for the specified locator. See Selenium api

You can use the original method name:

element.fireEvent('focus')

Or a more pythonic name:

element.fire_event('focus')

Both are equal to:

browser.execute('fireEvent', element.locator, 'focus')
attr(attr)

Return the attribute value of the element

drag_and_drop(element)

Drag and drop to element

eval(*expr)

Eval a javascript expression in Selenium RC. You can use the following variables:

  • s: the selenium object
  • b: the browserbot object
  • l: the element locator string
  • e: the element itself
exist()

return true is the element is present

hasClass(name)

True iif the class is present

html()

Return the innerHTML of the element

text()

Return the text of the element

wait(timeout=3000)

Wait for an element and return this element

wait_and_click(timeout=3000)

Wait for an element, click on it and return this element

class webtest.sel.Form(resp, id)

See Form

submit(name=None, index=None, extra_environ=None, timeout=None)

Submits the form. If name is given, then also select that button (using index to disambiguate)``.

Returns a webtest.browser.TestResponse object.

Examples

Testing a wsgi application

class TestApp(unittest.TestCase):

    def setUp(self):
        self.app = webtest.TestApp(application)

    def test_webtest(self):
        resp = self.app.get('/', {'redirect': '/message.html?message=submited'})
        resp.mustcontain('It Works!')
        form = resp.forms['myform']
        form.lint()

        self.assertEqual(form['mytext'].value, '')
        resp.mustcontain(no='Form submited')

        with webtest.selenium(resp) as sresp:
            if sresp:
                sform = sresp.forms['myform']
                sform['mytext'] = 'foo'
                sresp = sform.submit(name='go', timeout=0)
                sresp.mustcontain('Form submited')

        if resp.updated:
            resp.mustcontain('Form submited')
            form = resp.forms['myform']
            self.assertEqual(form['mytext'].value, 'foo')

        resp = form.submit(name='go')
        resp = resp.follow()
        resp.mustcontain('<pre>submited</pre>')

    @webtest.selenium
    def test_selenium(self):
        resp = self.app.get('/', {'redirect': '/message.html?message=submited'})
        resp.mustcontain('It Works!')
        form = resp.forms['myform']
        form.lint()

        form['mytext'] = 'foo'
        self.assertEqual(form['mytext'].value, 'foo')

        # file upload are only supported with *firefox *chrome drivers
        filename = os.path.join(files, 'html', 'index.html')
        file = form['myfile']
        file.value = (filename,)

        form['myradio'] = 'true'
        self.assertEqual(form['myradio'].value, 'true')
        check = form.get('mycheckbox', index=0)
        check.value = 'true'
        self.assertEqual(check.value, 'true')
        form['myselect'] = 'value2'
        form['myselect'] = 'value2'
        self.assertEqual(form['myselect'].value, 'value2')
        form['mymultiselect'] = ['value1', 'value3']
        self.assertEqual(form['mymultiselect'].value, ['value1', 'value3'])

        # there is an ajax hook on the page
        resp = form.submit(name='go', timeout=0)
        resp.mustcontain('Form submited')

        # but we can submit the form to get the non-javascript behavior
        resp = form.submit()
        resp = resp.follow()
        resp.mustcontain('<pre>submited</pre>')

Testing the jquery.ui website

class TestJQueryUI(unittest.TestCase):

    @classmethod
    def setupClass(cls):
        cls.app = webtest.SeleniumApp(url='http://jqueryui.com/')

    def setUp(self):
        self.resp = self.app.get('http://jqueryui.com/demos/')

    def test_autocomplete(self):
        resp = self.resp.click('Autocomplete')
        field = resp.doc.xpath('//input[@id="tags"]')
        field.value = 'a'
        item = resp.doc.xpath('//ul[@role="listbox"]//a[.="AppleScript"]')
        item.wait().fireEvent('mouseover')
        field.value = resp.doc.css('#ui-active-menuitem').html()
        self.assertEqual(field.value, "AppleScript")

    def test_datepicker(self):
        resp = self.resp.click('Datepicker')
        field = resp.doc.datepicker
        field.fireEvent('focus')
        resp.doc.link('16').wait_and_click()
        self.assertIn('/16/', field.value)

    def test_dialog(self):
        resp = self.resp.click('Dialog')
        close = resp.doc.xpath('//div[@role="dialog"]//span[.="close"]')
        close.wait_and_click()
        resp.doc.link('Modal form').click()
        resp.doc.button('Create new user').wait().click()
        form = resp.form
        form['name'].value = 'Gael'
        form['email'] = 'gael@gawel.org'
        create = resp.doc.button('Create an account')
        create.click()
        pwd = form['password']
        self.assertTrue(pwd.hasClass('ui-state-error'))
        pwd.value = 'pwd'
        create.click()
        resp.mustcontain('Length of password must be between 5 and 16.')
        pwd.value = 'passwd'
        create.click()
        resp.mustcontain('<td>Gael</td>')

    def test_dropable(self):
        resp = self.resp.click('Droppable')
        draggable = resp.doc.draggable
        droppable = resp.doc.droppable
        self.assertFalse(droppable.hasClass('ui-state-highlight'))
        draggable.drag_and_drop(droppable)
        self.assertTrue(droppable.hasClass('ui-state-highlight'))

        resp.doc.link('Shopping Cart').click()
        cart = resp.doc.css('#cart ol.ui-droppable')
        cart.wait()
        item = resp.doc.xpath('//li[.="Lolcat Shirt"]')
        self.assertNotIn(item, cart)
        item.drag_and_drop(cart)
        self.assertIn(item, cart)

    @classmethod
    def teardownClass(cls):
        cls.app.close()