Ticket #2879: 2879.selenium-support.4.diff

File 2879.selenium-support.4.diff, 28.6 KB (added by julien, 3 years ago)
  • django/conf/global_settings.py

    diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py
    index 6b09be2..ed72df9 100644
    a b DEFAULT_EXCEPTION_REPORTER_FILTER = 'django.views.debug.SafeExceptionReporterFil 
    560560# The name of the class to use to run the test suite
    561561TEST_RUNNER = 'django.test.simple.DjangoTestSuiteRunner'
    562562
     563# For the live test server (e.g. used for running Selenium tests)
     564LIVE_TEST_SERVER_HOST = 'localhost'
     565LIVE_TEST_SERVER_PORT = 8080
     566
     567# For Selenium
     568SELENIUM_SERVER_HOST = 'localhost'
     569SELENIUM_SERVER_PORT = 4444
     570SELENIUM_BROWSER = '*firefox'
     571
    563572############
    564573# FIXTURES #
    565574############
  • django/test/__init__.py

    diff --git a/django/test/__init__.py b/django/test/__init__.py
    index a3a03e3..b246821 100644
    a b Django Unit Test and Doctest framework. 
    44
    55from django.test.client import Client, RequestFactory
    66from django.test.testcases import (TestCase, TransactionTestCase,
    7         SimpleTestCase, skipIfDBFeature, skipUnlessDBFeature)
     7    SimpleTestCase, LiveServerTestCase, SeleniumTestCase, skipIfDBFeature,
     8    skipUnlessDBFeature)
    89from django.test.utils import Approximate
  • django/test/testcases.py

    diff --git a/django/test/testcases.py b/django/test/testcases.py
    index ee22ac2..aebb30a 100644
    a b  
    11from __future__ import with_statement
    22
     3import httplib
    34import re
    45import sys
    56from functools import wraps
    67from urlparse import urlsplit, urlunsplit
    78from xml.dom.minidom import parseString, Node
     9import select
     10import socket
     11import threading
    812
    913from django.conf import settings
     14from django.contrib.staticfiles.handlers import StaticFilesHandler
    1015from django.core import mail
    1116from django.core.exceptions import ValidationError
     17from django.core.handlers.wsgi import WSGIHandler
    1218from django.core.management import call_command
    1319from django.core.signals import request_started
     20from django.core.servers.basehttp import (WSGIRequestHandler, WSGIServer,
     21    WSGIServerException)
    1422from django.core.urlresolvers import clear_url_caches
    1523from django.core.validators import EMPTY_VALUES
    1624from django.db import (transaction, connection, connections, DEFAULT_DB_ALIAS,
    from django.test.utils import (get_warnings_state, restore_warnings_state, 
    2331    override_settings)
    2432from django.utils import simplejson, unittest as ut2
    2533from django.utils.encoding import smart_str
     34from django.utils.unittest import skipUnless
    2635
    2736__all__ = ('DocTestRunner', 'OutputChecker', 'TestCase', 'TransactionTestCase',
    2837           'SimpleTestCase', 'skipIfDBFeature', 'skipUnlessDBFeature')
    def skipUnlessDBFeature(feature): 
    732741    """
    733742    return _deferredSkip(lambda: not getattr(connection.features, feature),
    734743                         "Database doesn't support feature %s" % feature)
     744
     745class QuietWSGIRequestHandler(WSGIRequestHandler):
     746    """
     747    Just a regular WSGIRequestHandler except it doesn't log to the standard
     748    output any of the requests received, so as to not clutter the output for
     749    the tests' results.
     750    """
     751    def log_message(*args):
     752        pass
     753
     754class StoppableWSGIServer(WSGIServer):
     755    """
     756    The code in this class is borrowed from the `SocketServer.BaseServer` class
     757    in Python 2.6. The important functionality here is that the server is non-
     758    blocking and that it can be shut down at any moment. This is made possible
     759    by the server regularly polling the socket and checking if it has been
     760    asked to stop.
     761    Note for the future: Once Django stops supporting Python 2.5, this class
     762    can be removed as `WSGIServer` will have this ability to shutdown on
     763    demand.
     764    """
     765
     766    def __init__(self, *args, **kwargs):
     767        super(StoppableWSGIServer, self).__init__(*args, **kwargs)
     768        self.__is_shut_down = threading.Event()
     769        self.__serving = False
     770
     771    def serve_forever(self, poll_interval=0.5):
     772        """Handle one request at a time until shutdown.
     773
     774        Polls for shutdown every poll_interval seconds.
     775        """
     776        self.__serving = True
     777        self.__is_shut_down.clear()
     778        while self.__serving:
     779            r, w, e = select.select([self], [], [], poll_interval)
     780            if r:
     781                self._handle_request_noblock()
     782        self.__is_shut_down.set()
     783
     784    def shutdown(self):
     785        """Stops the serve_forever loop.
     786
     787        Blocks until the loop has finished. This must be called while
     788        serve_forever() is running in another thread, or it will
     789        deadlock.
     790        """
     791        self.__serving = False
     792        self.__is_shut_down.wait()
     793
     794    def handle_request(self):
     795        """Handle one request, possibly blocking.
     796        """
     797        fd_sets = select.select([self], [], [], None)
     798        if not fd_sets[0]:
     799            return
     800        self._handle_request_noblock()
     801
     802    def _handle_request_noblock(self):
     803        """Handle one request, without blocking.
     804
     805        I assume that select.select has returned that the socket is
     806        readable before this function was called, so there should be
     807        no risk of blocking in get_request().
     808        """
     809        try:
     810            request, client_address = self.get_request()
     811        except socket.error:
     812            return
     813        if self.verify_request(request, client_address):
     814            try:
     815                self.process_request(request, client_address)
     816            except Exception:
     817                self.handle_error(request, client_address)
     818                self.close_request(request)
     819
     820class LiveServerThread(threading.Thread):
     821    """
     822    Thread for running a live http server while the tests are running.
     823    """
     824
     825    def __init__(self, address, port, fixtures):
     826        self.address = address
     827        self.port = port
     828        self.fixtures = fixtures
     829        self.is_ready = threading.Event()
     830        self.error = None
     831        super(LiveServerThread, self).__init__()
     832
     833    def run(self):
     834        """
     835        Sets up live server and database and loops over handling http requests.
     836        """
     837        try:
     838            # Instantiate and start the server
     839            self.httpd = StoppableWSGIServer(
     840                (self.address, self.port), QuietWSGIRequestHandler)
     841            handler = StaticFilesHandler(WSGIHandler())
     842            self.httpd.set_app(handler)
     843
     844            # If the database is in memory we must reload the data in this new
     845            # thread.
     846            if (settings.DATABASES['default']['ENGINE'] == 'django.db.backends.sqlite3' or
     847                settings.DATABASES['default']['TEST_NAME']):
     848                connection.creation.create_test_db(0)
     849                # Import the fixtures into the test database
     850                if hasattr(self, 'fixtures'):
     851                    call_command('loaddata', *self.fixtures,
     852                        **{'verbosity': 0})
     853            self.is_ready.set()
     854            self.httpd.serve_forever()
     855        except WSGIServerException, e:
     856            self.error = e
     857            self.is_ready.set()
     858
     859    def join(self, timeout=None):
     860        self.httpd.shutdown()
     861        self.httpd.server_close()
     862        super(LiveServerThread, self).join(timeout)
     863
     864class LiveServerTestCase(TestCase):
     865    """
     866    Does basically the same as TestCase but also launches a live http server in
     867    a separate thread so that the tests may use another testing framework, such
     868    as Selenium for example, instead of the built-in dummy client.
     869    """
     870
     871    fixtures = []
     872
     873    def setUp(self):
     874        # Launch the Django live server's thread
     875        self.server_thread = LiveServerThread(
     876            settings.LIVE_TEST_SERVER_HOST,
     877            int(settings.LIVE_TEST_SERVER_PORT),
     878            fixtures=self.fixtures)
     879        self.server_thread.start()
     880        super(LiveServerTestCase, self).setUp()
     881
     882    def tearDown(self):
     883        # Terminate the Django server's thread
     884        self.server_thread.join()
     885        super(LiveServerTestCase, self).tearDown()
     886
     887try:
     888    # Check if the 'selenium' package is installed
     889    from selenium import selenium
     890    selenium_installed = True
     891except ImportError:
     892    selenium_installed = False
     893
     894# Check if the Selenium server is running
     895try:
     896    conn = httplib.HTTPConnection(settings.SELENIUM_SERVER_HOST,
     897        settings.SELENIUM_SERVER_PORT)
     898    try:
     899        conn.request("GET", "/selenium-server/driver/", '', {})
     900    finally:
     901        conn.close()
     902    selenium_server_running = True
     903except socket.error:
     904    selenium_server_running = False
     905
     906class SeleniumTestCase(LiveServerTestCase):
     907    """
     908    Does basically the same as TestServerTestCase but also connects to the
     909    Selenium server. The selenium client is then available with
     910    'self.selenium'. The requirements are to have the 'selenium' installed in
     911    the python path and to have the Selenium server running. If those
     912    requirements are not filled then the tests will be skipped.
     913    """
     914
     915    def setUp(self):
     916        super(SeleniumTestCase, self).setUp()
     917        # Launch the Selenium server
     918        selenium_browser_url = 'http://%s:%s' % (
     919            settings.LIVE_TEST_SERVER_HOST, settings.LIVE_TEST_SERVER_PORT)
     920        self.selenium = selenium(
     921            settings.SELENIUM_SERVER_HOST,
     922            int(settings.SELENIUM_SERVER_PORT),
     923            settings.SELENIUM_BROWSER,
     924            selenium_browser_url)
     925        self.selenium.start()
     926        # Wait for the Django server to be ready
     927        self.server_thread.is_ready.wait()
     928        if self.server_thread.error:
     929            raise self.server_thread.error
     930
     931    def tearDown(self):
     932        super(SeleniumTestCase, self).tearDown()
     933        self.selenium.stop()
     934
     935SeleniumTestCase = skipUnless(selenium_installed,
     936    'The \'selenium\' package isn\'t installed')(SeleniumTestCase)
     937SeleniumTestCase = skipUnless(selenium_server_running,
     938    'Can\'t connect to the Selenium server using address %s and port %s' % (
     939    settings.SELENIUM_SERVER_HOST, settings.SELENIUM_SERVER_PORT)
     940)(SeleniumTestCase)
     941 No newline at end of file
  • docs/internals/contributing/writing-code/unit-tests.txt

    diff --git a/docs/internals/contributing/writing-code/unit-tests.txt b/docs/internals/contributing/writing-code/unit-tests.txt
    index 5ec09fe..cec408c 100644
    a b Going beyond that, you can specify an individual test method like this: 
    122122
    123123    ./runtests.py --settings=path.to.settings i18n.TranslationTests.test_lazy_objects
    124124
     125Running the Selenium tests
     126~~~~~~~~~~~~~~~~~~~~~~~~~~
     127
     128Some admin tests require Selenium to work via a real Web browser. To allow
     129those tests to run and not be skipped, you must install the selenium_ package
     130into your Python path and download the `Selenium server (>2.12.0)`_. The
     131Selenium server must then be started with the following command:
     132
     133.. code-block:: bash
     134
     135    java -jar selenium-server-standalone-2.12.0.jar
     136
     137If you're using linux, you may also run the tests in headless mode (i.e. with a
     138virtual display) using with the following command instead:
     139
     140.. code-block:: bash
     141
     142    Xvfb :99 -ac & && DISPLAY=:99 java -jar selenium-server-standalone-2.12.0.jar
     143
     144Then, run the tests normally, for example:
     145
     146.. code-block:: bash
     147
     148    ./runtests.py --settings=test_sqlite admin_inlines
     149
    125150Running all the tests
    126151~~~~~~~~~~~~~~~~~~~~~
    127152
    dependencies: 
    135160*  setuptools_
    136161*  memcached_, plus a :ref:`supported Python binding <memcached>`
    137162*  gettext_ (:ref:`gettext_on_windows`)
     163*  selenium_ plus the `Selenium server (>2.12.0)`_
    138164
    139165If you want to test the memcached cache backend, you'll also need to define
    140166a :setting:`CACHES` setting that points at your memcached instance.
    associated tests will be skipped. 
    149175.. _setuptools: http://pypi.python.org/pypi/setuptools/
    150176.. _memcached: http://www.danga.com/memcached/
    151177.. _gettext: http://www.gnu.org/software/gettext/manual/gettext.html
     178.. _selenium: http://pypi.python.org/pypi/selenium
     179.. _Selenium server (>2.12.0): http://seleniumhq.org/download/
    152180
    153181.. _contrib-apps:
    154182
  • docs/ref/settings.txt

    diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
    index 20366e3..16caf48 100644
    a b all cache keys used by the Django server. 
    195195
    196196See the :ref:`cache documentation <cache_key_prefixing>` for more information.
    197197
     198.. setting:: LIVE_TEST_SERVER_HOST
     199
     200LIVE_TEST_SERVER_HOST
     201~~~~~~~~~~~~~~~~~~~~~
     202
     203Default: ``'localhost'``
     204
     205Controls the host address at which the live test server gets started when using
     206a :class:`~django.test.LiveServerTestCase`.
     207
     208See also: :setting:`LIVE_TEST_SERVER_PORT`
     209
     210.. setting:: LIVE_TEST_SERVER_PORT
     211
     212LIVE_TEST_SERVER_PORT
     213~~~~~~~~~~~~~~~~~~~~~
     214
     215Default: ``8080``
     216
     217Controls the port at which the live test server gets started when using
     218a :class:`~django.test.LiveServerTestCase`.
     219
     220See also: :setting:`LIVE_TEST_SERVER_HOST`
     221
    198222.. setting:: CACHES-LOCATION
    199223
    200224LOCATION
    Default: ``''`` (Empty string) 
    496520The port to use when connecting to the database. An empty string means the
    497521default port. Not used with SQLite.
    498522
     523.. setting:: SELENIUM_SERVER_HOST
     524
     525SELENIUM_SERVER_HOST
     526~~~~~~~~~~~~~~~~~~~~
     527
     528Default: ``localhost``
     529
     530Host address where the Selenium server can be accessed.
     531
     532.. setting:: SELENIUM_SERVER_PORT
     533
     534SELENIUM_SERVER_PORT
     535~~~~~~~~~~~~~~~~~~~~
     536
     537Default: ``4444``
     538
     539Port where the Selenium server can be accessed.
     540
     541.. setting:: SELENIUM_BROWSER
     542
     543SELENIUM_BROWSER
     544~~~~~~~~~~~~~~~~
     545
     546Default: ``'*firefox'``
     547
     548Browser to be used when running Selenium tests. Note that the prefixing star
     549('``*``') is required. Possible values include:
     550
     551*  ``'*firefox'``
     552*  ``'*googlechrome'``
     553*  ``'*safari'``
     554*  ``'*mock'``
     555*  ``'*firefoxproxy'``
     556*  ``'*pifirefox'``
     557*  ``'*chrome'``
     558*  ``'*iexploreproxy'``
     559*  ``'*iexplore'``
     560*  ``'*safariproxy'``
     561*  ``'*konqueror'``
     562*  ``'*firefox2'``
     563*  ``'*firefox3'``
     564*  ``'*firefoxchrome'``
     565*  ``'*piiexplore'``
     566*  ``'*opera'``
     567*  ``'*iehta'``
     568*  ``'*custom'``
     569
    499570.. setting:: USER
    500571
    501572USER
  • docs/topics/testing.txt

    diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt
    index dc5bf7e..9b9ed1c 100644
    a b Some of the things you can do with the test client are: 
    580580* Test that a given request is rendered by a given Django template, with
    581581  a template context that contains certain values.
    582582
    583 Note that the test client is not intended to be a replacement for Twill_,
     583Note that the test client is not intended to be a replacement for Windmill_,
    584584Selenium_, or other "in-browser" frameworks. Django's test client has
    585585a different focus. In short:
    586586
    587587* Use Django's test client to establish that the correct view is being
    588588  called and that the view is collecting the correct context data.
    589589
    590 * Use in-browser frameworks such as Twill and Selenium to test *rendered*
    591   HTML and the *behavior* of Web pages, namely JavaScript functionality.
     590* Use in-browser frameworks such as Windmill_ and Selenium_ to test *rendered*
     591  HTML and the *behavior* of Web pages, namely JavaScript functionality. Django
     592  also provides special support for those frameworks; see the sections on
     593  :class:`~django.test.testcases.LiveServerTestCase` and
     594  :class:`~django.test.testcases.SeleniumTestCase`.
    592595
    593596A comprehensive test suite should use a combination of both test types.
    594597
    595 .. _Twill: http://twill.idyll.org/
    596 .. _Selenium: http://seleniumhq.org/
    597 
    598598Overview and a quick example
    599599~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    600600
    set up, execute and tear down the test suite. 
    18281828    those options will be added to the list of command-line options that
    18291829    the :djadmin:`test` command can use.
    18301830
     1831Live test server
     1832----------------
     1833
     1834.. currentmodule:: django.test
     1835
     1836.. versionadded::1.4
     1837
     1838.. class:: LiveServerTestCase()
     1839
     1840``LiveServerTestCase`` does basically the same as
     1841:class:`~django.test.TestCase` with one extra thing: it launches a live Django
     1842server in the background on setup, and shuts it down on teardown. This allows
     1843to use other automated test clients than the
     1844:ref:`Django dummy client <test-client>` such as, for example, the Selenium_ or
     1845Windmill_ clients.
     1846
     1847You may control which host and port the live server will run at with
     1848respectively the :setting:`LIVE_TEST_SERVER_HOST` and
     1849:setting:`LIVE_TEST_SERVER_PORT` settings.
     1850
     1851Fixtures defined with the :attr:`~TestCase.fixtures` class attribute will get
     1852loaded at the beginning of each test if you require some initial data to be
     1853present.
     1854
     1855See the section on :class:`SeleniumTestCase` for a concrete example of how
     1856``LiveServerTestCase`` can be used.
     1857
     1858Selenium tests
     1859--------------
     1860
     1861.. versionadded::1.4
     1862
     1863.. class:: SeleniumTestCase()
     1864
     1865Django provides out-of-the box support for Selenium_ tests with the
     1866``SeleniumTestCase`` class. Django itself uses it in its own test suite for
     1867some ``contrib.admin`` tests.
     1868
     1869``SeleniumTestCase`` inherits from :class:`LiveServerTestCase`, which means
     1870that a live server is available for the duration of each test. That live server
     1871can then be accessed by the Selenium client to execute a series of functional
     1872tests inside a browser, simulating a real user's actions.
     1873
     1874To get started with Selenium tests, your environment needs to satisfy a number
     1875of requirements:
     1876
     1877*  You must install the `selenium package`_ into your Python path:
     1878
     1879   .. code-block:: bash
     1880
     1881       pip install selenium
     1882
     1883*  You must download the `Selenium server (>2.12.0)`_, and then start it with
     1884   the following command:
     1885
     1886   .. code-block:: bash
     1887
     1888       java -jar selenium-server-standalone-2.12.0.jar
     1889
     1890   If you'd like to run the selenium server at a different port than the
     1891   standard one (i.e. 4444) you may do so as follows:
     1892
     1893   .. code-block:: bash
     1894
     1895       java -jar selenium-server-standalone-2.12.0.jar -port 1234
     1896
     1897*  If the selenium server isn't running at the standard host address or port,
     1898   you need to provide the exact details using respectively the
     1899   :setting:`SELENIUM_SERVER_HOST` and :setting:`SELENIUM_SERVER_PORT`
     1900   settings.
     1901
     1902If those requirements are not satisfied, then the tests will be skipped.
     1903
     1904By the default, the tests are run in Firefox_. If you do not have Firefox
     1905installed or simply wish to run the tests in another browser, you may do so by
     1906changing the :setting:`SELENIUM_BROWSER` setting.
     1907
     1908Once your environment is set up, you may start writing your Selenium tests.
     1909Here's an example of how to control the Selenium client (accessible via
     1910``self.selenium``):
     1911
     1912.. code-block:: python
     1913
     1914    from django.test import SeleniumTestCase
     1915
     1916    class MySeleniumTests(SeleniumTestCase):
     1917
     1918        fixtures = ['user-data.json']
     1919
     1920        def test_login(self):
     1921            self.selenium.open('/login/')
     1922            self.selenium.type('username', username)
     1923            self.selenium.type('password', password)
     1924            self.selenium.click("//input[@value='Log in']")
     1925
     1926This is just a tiny fraction of what the Selenium client can do. Check out the
     1927`full reference`_ for more details.
     1928
     1929.. _Windmill: http://www.getwindmill.com/
     1930.. _Selenium: http://seleniumhq.org/
     1931.. _selenium package: http://pypi.python.org/pypi/selenium
     1932.. _Selenium server (>2.12.0): http://seleniumhq.org/download/
     1933.. _full reference: http://selenium.googlecode.com/svn/trunk/docs/api/py/selenium/selenium.selenium.html
     1934.. _Firefox: http://www.mozilla.com/firefox/
    18311935
    18321936Attributes
    18331937~~~~~~~~~~
    18341938
    1835 
    18361939.. attribute:: DjangoTestSuiteRunner.option_list
    18371940
    18381941    .. versionadded:: 1.4
  • tests/regressiontests/admin_inlines/tests.py

    diff --git a/tests/regressiontests/admin_inlines/tests.py b/tests/regressiontests/admin_inlines/tests.py
    index c2e3bbc..942ece6 100644
    a b from django.test import TestCase 
    77
    88# local test models
    99from .admin import InnerInline
     10from django.test.testcases import SeleniumTestCase
    1011from .models import (Holder, Inner, Holder2, Inner2, Holder3, Inner3, Person,
    1112    OutfitItem, Fashionista, Teacher, Parent, Child, Author, Book)
    1213
    class TestInlinePermissions(TestCase): 
    380381        self.assertContains(response, 'value="4" id="id_inner2_set-TOTAL_FORMS"')
    381382        self.assertContains(response, '<input type="hidden" name="inner2_set-0-id" value="%i"' % self.inner2_id)
    382383        self.assertContains(response, 'id="id_inner2_set-0-DELETE"')
     384
     385
     386class SeleniumTests(SeleniumTestCase):
     387    fixtures = ['admin-views-users.xml']
     388    urls = "regressiontests.admin_inlines.urls"
     389
     390    def admin_login(self, username, password):
     391        """
     392        Helper function to log into the admin.
     393        """
     394        self.selenium.open('/admin/')
     395        self.selenium.type('username', username)
     396        self.selenium.type('password', password)
     397        self.selenium.click("//input[@value='Log in']")
     398        self.selenium.wait_for_page_to_load(3000)
     399
     400    def test_add_inlines(self):
     401        """
     402        Ensure that the "Add another XXX" link correctly adds items to the
     403        inline form.
     404        """
     405        self.admin_login(username='super', password='secret')
     406        self.selenium.open('/admin/admin_inlines/titlecollection/add/')
     407
     408        # Check that there's only one inline to start with and that it has the
     409        # correct ID.
     410        self.failUnlessEqual(self.selenium.get_css_count(
     411            'css=#title_set-group table tr.dynamic-title_set'), 1)
     412        self.failUnless(self.selenium.get_attribute(
     413            'css=.dynamic-title_set:nth-of-type(1)@id'), 'title_set-0')
     414        self.failUnless(self.selenium.is_element_present(
     415            'css=form#titlecollection_form tr.dynamic-title_set#title_set-0 input[name=title_set-0-title1]'))
     416        self.failUnless(self.selenium.is_element_present(
     417            'css=form#titlecollection_form tr.dynamic-title_set#title_set-0 input[name=title_set-0-title2]'))
     418
     419        # Add an inline
     420        self.selenium.click("link=Add another Title")
     421
     422        # Check that the inline has been added, that it has the right id, and
     423        # that it contains the right fields.
     424        self.failUnlessEqual(self.selenium.get_css_count(
     425            'css=#title_set-group table tr.dynamic-title_set'), 2)
     426        self.failUnless(self.selenium.get_attribute(
     427            'css=.dynamic-title_set:nth-of-type(2)@id'), 'title_set-1')
     428        self.failUnless(self.selenium.is_element_present(
     429            'css=form#titlecollection_form tr.dynamic-title_set#title_set-1 input[name=title_set-1-title1]'))
     430        self.failUnless(self.selenium.is_element_present(
     431            'css=form#titlecollection_form tr.dynamic-title_set#title_set-1 input[name=title_set-1-title2]'))
     432
     433        # Let's add another one to be sure
     434        self.selenium.click("link=Add another Title")
     435        self.failUnlessEqual(self.selenium.get_css_count(
     436            'css=#title_set-group table tr.dynamic-title_set'), 3)
     437        self.failUnless(self.selenium.get_attribute(
     438            'css=.dynamic-title_set:nth-of-type(3)@id'), 'title_set-2')
     439        self.failUnless(self.selenium.is_element_present(
     440            'css=form#titlecollection_form tr.dynamic-title_set#title_set-2 input[name=title_set-2-title1]'))
     441        self.failUnless(self.selenium.is_element_present(
     442            'css=form#titlecollection_form tr.dynamic-title_set#title_set-2 input[name=title_set-2-title2]'))
     443
     444    def test_delete_inlines(self):
     445        self.admin_login(username='super', password='secret')
     446        self.selenium.open('/admin/admin_inlines/titlecollection/add/')
     447
     448        # Add a few inlines
     449        self.selenium.click("link=Add another Title")
     450        self.selenium.click("link=Add another Title")
     451        self.selenium.click("link=Add another Title")
     452        self.selenium.click("link=Add another Title")
     453        self.failUnlessEqual(self.selenium.get_css_count(
     454            'css=#title_set-group table tr.dynamic-title_set'), 5)
     455        self.failUnless(self.selenium.is_element_present(
     456            'css=form#titlecollection_form tr.dynamic-title_set#title_set-0'))
     457        self.failUnless(self.selenium.is_element_present(
     458            'css=form#titlecollection_form tr.dynamic-title_set#title_set-1'))
     459        self.failUnless(self.selenium.is_element_present(
     460            'css=form#titlecollection_form tr.dynamic-title_set#title_set-2'))
     461        self.failUnless(self.selenium.is_element_present(
     462            'css=form#titlecollection_form tr.dynamic-title_set#title_set-3'))
     463        self.failUnless(self.selenium.is_element_present(
     464            'css=form#titlecollection_form tr.dynamic-title_set#title_set-4'))
     465
     466        # Click on a few delete buttons
     467        self.selenium.click(
     468            'css=form#titlecollection_form tr.dynamic-title_set#title_set-1 td.delete a')
     469        self.selenium.click(
     470            'css=form#titlecollection_form tr.dynamic-title_set#title_set-2 td.delete a')
     471        # Verify that they're gone and that the IDs have been re-sequenced
     472        self.failUnlessEqual(self.selenium.get_css_count(
     473            'css=#title_set-group table tr.dynamic-title_set'), 3)
     474        self.failUnless(self.selenium.is_element_present(
     475            'css=form#titlecollection_form tr.dynamic-title_set#title_set-0'))
     476        self.failUnless(self.selenium.is_element_present(
     477            'css=form#titlecollection_form tr.dynamic-title_set#title_set-1'))
     478        self.failUnless(self.selenium.is_element_present(
     479            'css=form#titlecollection_form tr.dynamic-title_set#title_set-2'))
     480 No newline at end of file
  • tests/regressiontests/admin_widgets/tests.py

    diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py
    index 08a1a59..c0704d5 100644
    a b from django.core.files.storage import default_storage 
    1111from django.core.files.uploadedfile import SimpleUploadedFile
    1212from django.db.models import DateField
    1313from django.test import TestCase as DjangoTestCase
     14from django.test.testcases import SeleniumTestCase
    1415from django.utils import translation
    1516from django.utils.html import conditional_escape
    1617from django.utils.unittest import TestCase
    class RelatedFieldWidgetWrapperTests(DjangoTestCase): 
    372373        # Used to fail with a name error.
    373374        w = widgets.RelatedFieldWidgetWrapper(w, rel, widget_admin_site)
    374375        self.assertFalse(w.can_add_related)
     376
     377
     378class SeleniumTests(SeleniumTestCase):
     379    fixtures = ['admin-widgets-users.xml']
     380    urls = "regressiontests.admin_widgets.urls"
     381
     382    def admin_login(self, username, password):
     383        """
     384        Helper function to log into the admin.
     385        """
     386        self.selenium.open('/')
     387        self.selenium.type('username', username)
     388        self.selenium.type('password', password)
     389        self.selenium.click("//input[@value='Log in']")
     390        self.selenium.wait_for_page_to_load(3000)
     391
     392    def get_css_value(self, selector, attribute):
     393        """
     394        Helper function that returns the value for the CSS attribute of an
     395        DOM element specified by the given selector.
     396        """
     397        return self.selenium.get_eval(
     398            'selenium.browserbot.getCurrentWindow().django'
     399            '.jQuery("%s").css("%s")' % (selector, attribute))
     400
     401    def test_show_hide_date_time_picker_widgets(self):
     402        """
     403        Ensure that pressing the ESC key closes the date and time picker
     404        widgets.
     405        Refs #17064.
     406        """
     407        self.admin_login(username='super', password='secret')
     408        # Open a page that has a date and time picker widgets
     409        self.selenium.open('/admin_widgets/member/add/')
     410
     411        # First, with the date picker widget ---------------------------------
     412        # Check that the date picker is hidden
     413        self.assertEqual(
     414            self.get_css_value('#calendarbox0', 'display'), 'none')
     415        # Click the calendar icon
     416        self.selenium.click('id=calendarlink0')
     417        # Check that the date picker is visible
     418        self.assertEqual(
     419            self.get_css_value('#calendarbox0', 'display'), 'block')
     420        # Press the ESC key
     421        self.selenium.key_up('css=html', '27')
     422        # Check that the date picker is hidden again
     423        self.assertEqual(
     424            self.get_css_value('#calendarbox0', 'display'), 'none')
     425
     426        # Then, with the time picker widget ----------------------------------
     427        # Check that the time picker is hidden
     428        self.assertEqual(
     429            self.get_css_value('#clockbox0', 'display'), 'none')
     430        # Click the time icon
     431        self.selenium.click('id=clocklink0')
     432        # Check that the time picker is visible
     433        self.assertEqual(
     434            self.get_css_value('#clockbox0', 'display'), 'block')
     435        # Press the ESC key
     436        self.selenium.key_up('css=html', '27')
     437        # Check that the time picker is hidden again
     438        self.assertEqual(
     439            self.get_css_value('#clockbox0', 'display'), 'none')
     440 No newline at end of file
Back to Top