Selenium test runner for Zope 3
===============================

The selenium test runner makes it a bit easier to run selenium tests
by automatically running a Zope server and causing a browser to load
and run the test.

That's the good news. :)  There are, unfortunately, a number of
steps/tricks required to make this work.

Zope 3 configuration
--------------------

You need to create a Zope server that uses the zc.selenium package and
a demo-storage database.  Here's an example buildout::

  [buildout]
  develop = .
  parts = test py demo selenium

  [test]
  recipe = zc.recipe.testrunner
  eggs = zc.extjs [test]

  [application]
  recipe = zc.zope3recipes:application
  servers = zserver
  eggs =
       ${test:eggs}
       zope.app.server
       zc.selenium
  site.zcml = <include package="zc.extjs" file="selenium.zcml" />

  [demo]
  recipe = zc.zope3recipes:instance
  application = application
  zope.conf =
    devmode on
    <zodb>
      <demostorage>
      </demostorage>
    </zodb>
    <server http0>
      address 39857
      type WSGI-HTTP
    </server>

  [selenium]
  recipe = zc.recipe.egg:scripts
  eggs = ${application:eggs}
  script = selenium
  entry-points = selenium=zc.selenium.selenium:main
  initialization =
    sys.argv[1:1] = ['${demo:run-directory}/zope.conf', '-r']

This is for a demo application for a package named zc.extjs.  Note
that we have an application definition that uses zope.app.server and
zc.selenium. The selenium.zcml used in this example look like this::

  <configure xmlns="http://namespaces.zope.org/zope" package="zc.extjs">

    <include file="tests.zcml" />

    <include package="zope.app.server" />
    <include package="zc.selenium" />

    <adapter
       factory="zc.extjs.selenium.SeleniumTests"
       name="zc.extjs.tests.SeleniumTests.html"
       permission="zope.Public"
       />

  </configure>

Note that it included zope.app.server and zc.selenium.  This example
also shows an adapter  registration for a Python-based test. Note that
the test runner currently requires that the zope.server server is used!

The buildout defines an instance with the demo part.  Two things to
note about this:

1. A demo storage is used.  This is important because it guarantees we
   have the same baseline for each test.

2. We have the name "http0" for the server.  This name is used by the
   startup program to specify the port to use when the server is
   started to run the tests.  Without this, you'll an odd error when
   you try to run the tests.

The use of http0 is not ideal.  In the future, when we have time,
we'll want to do something better.

The selenium script
-------------------

The selenium script needs to be defined.  It runs the server as a
thread, rather than as a sub-process.  For this reason, it needs to
have the same packages as the application.  In the buildout above,
we've defined the selenium script using the zc.recipe.egg:scripts
recipe.  We specified the same eggs as the application part.  We also
supply the demo-instance configuration file as the implicit first
argument to the script and the "-r" as the second argument.

Once the script is installed, just run it to run the test.

You can run the script with the -h option to find out about other
options. Note, however, that if you bake the -r option into the
script, as I have, you won't be able to override it with the -p
option. (This is possibly a bug.)

Oddities
--------

The selenium script automatically selects the browser to use. If you're
lucky, this will turn out well. :)  You can control the browser
used. The most reliable way to do this is with the BROWSER environment
variable::

   BROWSER=firefox bin/selenium

There's also a -b option to the script that could theoretically work.
This to requires some luck as it depends on Python sniffing out
available browsers correctly.  I recommend using the environment
variable.


Future work
-----------

I think this could and should be cleaned up a bit in the future:

- Have the test runner be a control script that simply starts the
  instance as a sub-process, rather than a thread.

- Find a better way to control the port used.
